前言

描述: Spring Security 入门系列文章。

1 SpringSecurity 简介

springsecurity的认证逻辑 spring security认证_spring

1.1 框架简介

Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
正如你可能知道的关于安全方面的两个主要区域是“认证”和“授权”(或者访问控制),一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是 Spring Security 重要核心功能。
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情。

1.2 Spring Security和Shiro比较

在 Java 生态中,目前有 Spring Security 和 Apache Shiro 两个安全框架,可以完成认证和授权的功能。

相同点

1:认证功能
2:授权功能
3:加密功能
4:会话管理
5:缓存支持
6:rememberMe功能.......

不同点
优点:

1:Spring Security基于Spring开发,项目中如果使用Spring作为基础,配合Spring Security做权限更加
方便,而Shiro需要和Spring进行整合开发
2:Spring Security功能比Shiro更加丰富些,例如安全防护
3:Spring Security社区资源比Shiro丰富

缺点:

1:Shiro的配置和使用比较简单,Spring Security上手复杂
2:Shiro依赖性低,不需要任何框架和容器,可以独立运行,而Spring Security依赖于Spring容器

一般来说,常见的安全管理技术栈的组合是这样的:

SSM + Shiro 
Spring Boot/Spring Cloud +Spring Security

1.3 权限管理中的相关概念

1.3.1 主体

英文单词:principal
使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。

1.3.2 认证

英文单词:authentication
权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。笼统的认为就是以前所做的登录操作。

1.3.3 授权

英文单词:authorization
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。

2 简单用户认证

springsecurity的认证逻辑 spring security认证_自定义_02

2.1 pom

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <!--公用依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.71</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.2 application.yml

server:
  port: 8888

2.3 SpringSecurity 用户自定义配置类

描述: 用户需要继承WebSecurityConfigurerAdapter来实现自定义配置。

/**
 * @Description: Spring Security 自定义配置类
 * @Author: rosh
 * @Date: 2021/4/10 11:16
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    @Qualifier("roshUserDetailService")
    private RoshUserDetailService roshUserDetailService;

    /**
     *  配置登录用户名、密码及角色
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(roshUserDetailService).passwordEncoder(passwordEncoder());
    }


    /**
     * 配置加密方式,官方推荐加密方式为BCrypt
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

2.4 用户自定义认证方式

描述: 用户需要实现UserDetailsService 来实现自定义用户认证

/**
 * @Description: 自定义认证类
 * @Author: rosh
 * @Date: 2021/4/11 10:30
 */
@Service("roshUserDetailService")
public class RoshUserDetailService implements UserDetailsService {

    /**
     * UserDetails:返回用户的主体
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //配置一个admin、123456的用户
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        return new User("admin", new BCryptPasswordEncoder().encode("123456"), auths);
    }
}

2.5 相关业务类

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Student {

    private Integer id;

    private String name;

    private String hobby;

}


@RestController
@RequestMapping("/student")
public class StudentController {


    @GetMapping("/{id}")
    public Student getStudent(@PathVariable("id") Integer id) {
        return new Student(id, "rosh", "basketBall");
    }

}

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

}

2.6 测试

访问接口:http://localhost:8888/student/1

springsecurity的认证逻辑 spring security认证_spring_03


描述: 输入admin、123456

springsecurity的认证逻辑 spring security认证_springsecurity的认证逻辑_04

3 数据库认证

3.1 数据库环境

-- 创表语句
CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- 插入管理员数据admin、123456
insert into t_user(username,password) VALUES('admin','$2a$10$YDq3287Iw86gFr3RyKl7fe7JYQCkoic5K0XOgoSDpJgQEdYkWvxKG')

springsecurity的认证逻辑 spring security认证_spring_05

3.2 更新pom文件

<!--数据库-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

springsecurity的认证逻辑 spring security认证_ci_06

3.3 修改application.yml

server:
  port: 8888

spring:
    #数据库连接池
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
mybatis-plus:
  #默认值classpath*:/mapper/**/*.xml
  mapper-locations: classpath:/mapper/*.xml
  #配置全局自增ID
  global-config:
    db-config:
      id-type: auto

3.4 mybatis业务类

springsecurity的认证逻辑 spring security认证_spring_07

@Data
@TableName(value = "t_user")
public class UserEntity {

    @TableId
    private Integer id;

    private String username;

    private String password;

}
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
}

@Service
public class UserService extends ServiceImpl<UserMapper, UserEntity> {

    public UserEntity findUserByUsername(@NonNull final String username) {
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        return baseMapper.selectOne(wrapper);
    }
}

3.5 修改RoshSerucityApplication

@SpringBootApplication
@MapperScan("com.rosh.security.mapper")
public class RoshSerucityApplication {

    public static void main(String[] args) {
        SpringApplication.run(RoshSerucityApplication.class);
    }

}

3.6 修改用户自定义认证类

描述: 修改RoshUserDetailService

@Service("roshUserDetailService")
public class RoshUserDetailService implements UserDetailsService {

    @Autowired
    private UserService userService;

    /**
     * UserDetails:返回用户的主体
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //1 查询db
        UserEntity user = userService.findUserByUsername(username);

        //2 不存在认证失败
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        //3 校验
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        return new User(user.getUsername(), user.getPassword(), auths);
    }
}

3.7 测试

http://localhost:8888/student/1

描述: 错误登录

springsecurity的认证逻辑 spring security认证_自定义_08

描述: 正确登录

springsecurity的认证逻辑 spring security认证_自定义_09

4 自定义登录页面及访问控制

4.1 修改Spring Security 自定义配置类

描述: 重写configure方法。

springsecurity的认证逻辑 spring security认证_springsecurity的认证逻辑_10

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    @Qualifier("roshUserDetailService")
    private RoshUserDetailService roshUserDetailService;

    /**
     *  配置登录用户名、密码及角色
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(roshUserDetailService).passwordEncoder(passwordEncoder());
    }


    /**
     * 配置加密方式,官方推荐加密方式为BCrypt
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 自定义页面配置、登录访问配置
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.formLogin()
                //配置登录页面
                .loginPage("/login.html")
                //登录访问路径
                .loginProcessingUrl("/user/login").permitAll()
                //登录成功访问接口
                .defaultSuccessUrl("/user/login/success")
                //登录失败访问的接口
                .failureForwardUrl("/user/login/failed")
                //配置url访问权限,登录url可以直接访问,不需要认证
                .and().authorizeRequests().antMatchers("/login.html", "/user/login/failed").permitAll()
                //其余url需要认证才能访问
                .anyRequest().authenticated()
                //关闭csrf
                .and().csrf().disable();
    }
}

4.2 登录页面

springsecurity的认证逻辑 spring security认证_自定义_11


描述: 表单方法必须为post,name必须为username,password。查看核心过滤器,UsernamePasswordAuthenticationFilter。

springsecurity的认证逻辑 spring security认证_spring_12

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>


<form action="/user/login"  method="post">
    用户名:<input type="text" name="username">
    <br/>
    密码: <input type="text" name="password">
    <br/>
    <input type="submit" value="登录">
</form>


</body>
</html>

4.3 相关业务类

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/login/success")
    public String loginSuccess() {
        return "用户认证成功";
    }

    @PostMapping("/login/failed")
    public String loginFailed() {
        return "用户认证失败";
    }


}

4.4 测试

描述: 直接访问student接口:http://localhost:8888/student/1会重定向到登录页面

springsecurity的认证逻辑 spring security认证_spring_13


springsecurity的认证逻辑 spring security认证_springsecurity的认证逻辑_14


描述: 登录失败测试。

springsecurity的认证逻辑 spring security认证_springsecurity的认证逻辑_15


springsecurity的认证逻辑 spring security认证_ci_16


描述: 登录成功测试

springsecurity的认证逻辑 spring security认证_spring_17


springsecurity的认证逻辑 spring security认证_ci_18