SpringSecurity-认证流程第二章

一.认证授权流程
1.认证授权流程
SpringSecurity是基于Filter实现认证和授权,底层通过FilterChainProxy代理去调用各种Filter(Filter链),Filter通过调用AuthenticationManager完成认证 ,通过调用AccessDecisionManager完成授权。流程如下图:
SpringSecurity-认证流程第二章2.Security过滤器链
我们知道,SpringSecurity是通过很多的过滤器链共同协作完成认证,授权的流程,SpringSecurity中核心的过滤器链如下:
SpringSecurity-认证流程第二章3.SpringSecurity认证流程原理
SpringSecurity-认证流程第二章4.定义认证流程
在SpringSecurity的整个认证流程中,除了UserDetailsService需要我们自己定义外,其他的的组件都可以使用默认的,因为UserDetailsService是SpringSecurity获取数据库中的认证信息的媒介,而如何才能从数据库中获取认证信息只有我们才知道。在入门案例中我们使用的是InMemoryUserDetailsManager 基于内存的UserDetailsService方案,接下来我们需要把基于内存的方案修改为基于数据库的方案。
5.定义UserDetailsService

UserDetailsService
是SpringSecurity提供用来获取认证用户信息(用户名,密码,用户的权限列表)的 接 口,我们可以实现该接口,复写loadUserByUsername(username) 方法加载我们数 据 库中的用户信息

UserDetails
UserDetails是SpringSecurity用来封装用户认证信息,权限信息的对象,我们使用它 的实现类User封装用户信息 并返回,我们这里从数据库查询用户名

5.创建类UserDetailServiceImpl实现UserDetailsService接口

package cn.x.th.userService;

import cn.x.th.domain.Permission;
import cn.x.th.domain.VipUser;
import cn.x.th.mapper.VipUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private VipUserMapper vipUserMapper;

    @Override//根据参数中的用户名,来查询用户的对象信息
    //1.整个方法体中,并没有对前端传过来的密码进行校验。
    //2.前端传过来的用户密码,后台代码根本就没有提供获取的方法。
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        VipUser vipUser = vipUserMapper.findUserByUserName(userName);
        List<GrantedAuthority> authorityList = new ArrayList<>();//在传入时,会同时查询出当前用户的权限列表
        //查询当前用户所有的权限
        //在security进行授权管理时,就要在这里将当前登录用户的权限列表给查出来
        List<Permission> permissionList = vipUserMapper.findPermissionListByUserName(userName);
        //for (Permission permission:permissionList) {
            //将查询来的权限逐一添加到authorityList中
            //authorityList.add(new SimpleGrantedAuthority(permission.getExpression()));
       // }
        //密码最终校验的地方是由Security框架完成。 原始密码保存在SecurityContext上下文,传入一个加密之后的密码:vipUser.getPassword()
        return new User(userName, vipUser.getPassword(),authorityList);
    }
}

注意:这里定义了UserDetailSerice后,WebSecurityConfig中不在需要定义UserDetailService的Bean需要移除
6.导入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- swagger-ui的依赖-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- springsecurity权限框架的环境依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>


        <!-- 导入mybatis-plus相关的依赖-->
        <!-- 导入数据库连接相关的依赖-->
        <!-- 数据库驱动和druid-->
        <!-- 注意坑: mybatis-plus3.x后,在自动生成的实体类中,对日期类型进行了升级,使用的是jdk1.8的LocalDate
     要支持这种数据类型,必须对连接池druid和mysql驱动进行升级,升级版本如下:
     -->
        <!-- MySql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>
        <!-- druid连接池  这个starter的坐标实际上也只是导入了关联版本的druid连接池坐标,所以这里直接用导入druid坐标替代-->
        <!--<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!-- mybatis-plus3的依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
    </dependencies>

注意:检查父工程有没有继承下面的依赖

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

7.yml配置

server:
  port: 1200
#配置数据库的连接
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 连接池指定 springboot2.02版本默认使用HikariCP 此处要替换成Druid
    driver-class-name: com.mysql.cj.jdbc.Driver # 这个驱动必须用新版,不能用老版
    url: jdbc:mysql:///hrm-security?characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 123456
    druid:
      initial-size: 5 # 初始化时建立物理连接的个数
      min-idle: 5 # 最小连接池连接数量,最小空闲数量
      max-active: 20 # 最大连接池连接数量,最大活跃连接数
      max-wait: 60000 # 配置获取连接等待超时的时间
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1
      testWhileIdle: true
      testOnBorrow: true
      testOnReturn: false
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      filters: stat,wall
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      stat-view-servlet:
        allow: 0.0.0.0 # 允许哪些IP访问druid监控界面,多个IP以逗号分隔
        login-username: admin # 设置登录帐号
        login-password: 123456 # 设置登录密码
        reset-enable: false # 是否允许重置数据
        # url-pattern: /database/* # 默认访问根路径是:/druid/;也可以自定义设置
mybatis-plus: # mybatis的配置。下面的配置希望执行Sql语句可以打印到 控制台
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 开启mybatis-plus的sql打印

8.编写Mapper.xml
9.编写Mapper映射器接口
10.创建数据库 :sql见资料:auth-rbac.sql(可在资源里下载)
11.定义密码编码器

@Bean
public PasswordEncoder passwordEncoder(){
    //return NoOpPasswordEncoder.getInstance();//不加密
    return new BCryptPasswordEncoder();//加密
}

12.自定义登录页面
resource里准备登录页面static/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>登陆</h1>
<form method="post" action="/login">
    <div>
        用户名:<input type="text" name="username">
    </div>
    <div>
        密码:<input type="password" name="password">
    </div>
    <div>
        <button type="submit">立即登陆</button>
    </div>
</form>
</body>
</html>
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启授权
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//密码编码器:不加密(权限框架提供了设置密码加密方式的配置对象)
    @Bean
    public PasswordEncoder passwordEncoder(){
//        return NoOpPasswordEncoder.getInstance();//不加密
        return new BCryptPasswordEncoder();//加密
    }
    
    //授权规则配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()                                //授权配置
                .antMatchers("/login","/login.html").permitAll()  //登录路径放行
                .anyRequest().authenticated()     //其他路径都要认证之后才能访问
                .and().formLogin()                              //允许表单登录
                .loginPage("/login.html")                        //这个就是自定义的登录页面
                .loginProcessingUrl("/login")                   //告诉框架,现在的登录请求的URL地址是:/login
                .successForwardUrl("/loginSuccess")             // 设置登陆成功页
                .successHandler(new MyAuthenticationSuccessHandler())//设置认证成功后,handler的处理器
                .failureHandler(new MyAuthenticationFailureHandler())//设置认证失败后handler的处理器
                .and().logout().permitAll()                    //登出路径放行 /logout。这是框架自带的登出请求
                .and().csrf().disable();                        //关闭跨域伪造检查
	}
}

13.mapper层

public interface VipUserMapper extends BaseMapper<VipUser> {
    @Select("select * from t_vip_user where username = #{userName}")
    VipUser findUserByUserName(String username);

    //List<Permission> findPermissionListByUserName(String userName);
}

14.测试

上一篇:springsecurity-用户注销


下一篇:SpringSecurity