SpringSecurity跟SpringBoot的整合-项目创建
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)
 - 添加硬编码用户测试
 - 权限根据角色控制
 - 自定义错误页面
 
后面我们再去自定义登录界面吧,在下一篇文章再去从数据库中获取用户信息和用户角色。
