阳光沙滩
让学习编程变得简单
SpringSecurity跟SpringBoot的整合-项目创建
发表于 2020-03-20    阅读次数 342

SpringSecurity跟SpringBoot的整合

好想每次都想写系列文章,总是被各种事情打乱,然后就忘记了。在我的草稿箱里,还躺着其他的系列文章,哈哈。

希望这个系列可以写完吧。

创建一个Maven项目

这里就不说创建了,就把pom.xml贴出来吧

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>


    <groupId>net.sunofbeach</groupId>
    <artifactId>springsecuritydemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>sob-security-demo</name>
    <url>https://www.sunofbeach.net</url>
    <inceptionYear>2020-Now</inceptionYear>


    <properties>

        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

        <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    </properties>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>sob-security-demo-1.0.0</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>net.sunofbeach.security.App</mainClass>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

把这里的依赖复制粘贴到你的项目里吧

主app程序

package net.sunofbeach.security;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

然后创建测试的controller,security包,分别放置controller和security相关的配置文件之类的

配置登录方式

当我们在依赖里添加了spring-security的依赖(坐标)

导入以后,运行程序,就会被拦截所有的请求

跳转到登录页面

图片描述

从log上可以看到登录密码

我们打开网页时,比如说我的controller里配置了一个index的页面

我要访问这个页面,就会跳转到登录了 图片描述

package net.sunofbeach.security.security;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //拦截所有
                .antMatchers("/**")
                .fullyAuthenticated()
                .and()
                //.httpBasic(); 
                .formLogin();
    }
}

这里我改成了formLogin,也就是表单登录方式

再跑一次,变成这个样子了

图片描述

配置账号密码

我覆写了另外一个方法

   @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .passwordEncoder(new MyPasswordEncoder())
                .withUser("test")
                .password("123456")
                .authorities("admin");
    }

这个就是添加一个硬编码的用户,后面我们要动态地从数据中查询出来

这个MyPasswordEncoder是继承自BCryptPasswordEncoder

代码如下:

package net.sunofbeach.security.security;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class MyPasswordEncoder extends BCryptPasswordEncoder {

    @Override
    public String encode(CharSequence rawPassword) {
        return rawPassword.toString();
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(rawPassword.toString());
    }
}

也就是没有进行加密行为,是明文。

这个时候我们再设置一下,登录成功以后,跳转到什么页面

   @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //拦截所有
                .antMatchers("/**")
                .fullyAuthenticated()
                .and()
                //.httpBasic();
                .formLogin()
                .defaultSuccessUrl("/index");
    }

登录成功以后,跳转到首页去

图片描述

如何根据权限判断是否放行呢?

先整些用户

  @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //添加一个注册会员用户
        auth.inMemoryAuthentication()
                .passwordEncoder(new MyPasswordEncoder())
                .withUser("test")
                .password("123456")
                .authorities("register");

        //添加一个管理员帐户
        auth.inMemoryAuthentication().passwordEncoder(new MyPasswordEncoder())
                .withUser("admin")
                .password("admin")
                .authorities("register", "admin");
    }

然后配置规则

   @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //拦截所有
                .antMatchers("/manager").hasAuthority("admin")//admin 角色才可以访问
                .antMatchers("/list").hasAuthority("register")//注册用户就可以访问
                .antMatchers("/**")
                .fullyAuthenticated()
                .and()
                //.httpBasic();
                .formLogin()
                .defaultSuccessUrl("/index");
    }

运行,登录,访问

我们使用test帐户时,访问list是正常的,访问manager则会报错 403,权限不足的错误

图片描述

但是,我们总不能让用户跑到403界面吧?

所以我们是不是要跳转到自定义的界面呢?我们自己写一个权限不足的页面

处理错误状态码

比如说403,我们总不能让用户看到403页面吧

应该写一个提示权限不足的页面

有些同学可能知道EmbeddedServletContainerCustomizer,但是自从spring boot2.0以上的版本

我们实现这个接口来配置错误页面ErrorPageRegistrar

所以我们接下来应该这么写

@Configuration
public class MyErrorPage implements ErrorPageRegistrar {
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403"));
    }
}

这个addErrorPages,参数是不定参数,可以有多个,如果你要处理各种http状态,就可以使用这个啦。

处理结果:

图片描述

总结

我们完成了哪些工作呢?

  • SpringBoot项目创建
  • 自定义登录方式(httpBasic,formLogin)
  • 添加硬编码用户测试
  • 权限根据角色控制
  • 自定义错误页面

后面我们再去自定义登录界面吧,在下一篇文章再去从数据库中获取用户信息和用户角色。