SpringSecurity权限框架用法(认证与授权流程)

授权中什么是RBAC

RBAC是基于角色的访问控制Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。

单词汇总

Authorization :授权
Authentication :认证(登录)
WebSecurityConfigurerAdapter : web安全配置类(认证授权配置)
@EnableWebSecurity :开启web安全配置
HttpSecurity : http安全配置
permitAll : 放行
antMatchers : 用来匹配某些url路径
UserDetailsService :用户详情服务(用来加载数据库的用户数据) UserDao
SecurityContext : Security上下文工具,保存得有认证成功之后的用户信息

SpringSecurity概述

SpringSecurity介绍

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.zxq</groupId>
            <artifactId>hrm-basic-util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </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>

启动类

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

application.yml配置

server:
  port: 1110
#配置数据库的连接
spring:
  application:
    name: hrm-system
  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打印

config中swagger-ui的配置

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        ApiInfo apiInfo = new ApiInfoBuilder()
                .title("服务接口文档范例")
                .description("springboot集成框架案例")
                .version("1.0")
                .build();

        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo)
                .select()
                //以扫描包的方式
                .apis(RequestHandlerSelectors.basePackage("cn.zxq"))
                .paths(PathSelectors.any())
                .build();
    }
}

配置SpringSecurity

SpringSecurity提供了一个配置类WebSecurityConfigurerAdapter用来提供给程序员对SpringSecurity做自定义配置,我们需要配置如下几个信息:

  • 创建UserDetailService的Bean,该组件是用来加载用户认证信息
  • 配置编码器,通过该编码器对密码进行加密匹配。
  • 授权规则配置,哪些资源需要什么权限..

以下WebSecurityConfig的配置     认证、授权、记住我、全部以实现

@Configuration
@EnableWebSecurity
//@EnableGlobalMethodSecurity(securedEnabled=true) //这个注解需要在配置类上开启授权注解支持
@EnableGlobalMethodSecurity(prePostEnabled=true) //PreAuthorize适合进入方法前的权限验证,拥有和Secured同样的功能,甚至更强大
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // 一旦从数据库查询用户信息后,就将当前这个@Bean注释掉,否则 会影响程序的效果
    //提供用户信息,这里没有从数据库查询用户信息,在内存中模拟
    //它就是用来交给spring创建UserDetails用户对象信息的工具类
   /**@Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager =
        new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("zs").password("123").authorities("admin").build());
        return inMemoryUserDetailsManager;
    }*/
  

    //密码编码器:不加密(权限框架提供了设置密码加密方式的配置对象)
    @Bean
    public PasswordEncoder passwordEncoder(){
         //return NoOpPasswordEncoder.getInstance();
        return new BCryptPasswordEncoder();
    }
    
    //授权规则配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()                                //授权配置
                .antMatchers("/login").permitAll()  //登录路径放行
                .antMatchers("/login.html").permitAll() //对登录页面跳转路径放行
                //进行授权的配置:当要访问资源【/employee/list】,需要当前用户具备权限:employee:list
                //现在数据库中,只有zs才有这个权限。最终的效果:张三能正常访问,但李四不可以
                //配置访问的资源对应的权限配置,必须要放在.anyRequest().authenticated() 之前
                //在配置类中,针对请求的方法进行权限控制:它是全局的。是程序在启动时就已经加载。
                //总结:所有资源权限控制,都必须要在这里配置,才能实现全局控制。
                // 问题:所有开发人员,要对自己的资源进行控制,都必须改这个配置类,不安全,容易改错。
                //.antMatchers("/employee/list/**").hasAuthority("employee:list")
                .anyRequest().authenticated()     //其他路径都要认证之后才能访问
                .and().formLogin()                              //允许表单登录
                .loginPage("/login.html")   //登录页面跳转地址
                .loginProcessingUrl("/login")   //登录处理地址(必须)
                .successForwardUrl("/loginSuccess")             // 设置登陆成功页
                .successHandler(new MyAuthenticationSuccessHandler()) //设置认证成功后,handler的处理器
                .failureHandler(new MyAuthenticationFailureHandler()) //设置认证失败后handler的处理器
                .and().logout().permitAll()                    //登出路径放行 /logout
                .and().csrf().disable();                        //关闭跨域伪造检查
        //配置权限不足时,自定义结果返回给前端
        http.exceptionHandling().accessDeniedHandler(new DefaultAccessDeniedHandler());
        //配置记住我功能 ,参数1:3600单位是秒;参数2:userDetailsService 自定义的userDetailServiceImpl
        http.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(3600)	//过期时间
                .userDetailsService(userDetailsService); //用来加载用户认证信息的
        //进行授权的配置:当要访问资源employee/list  需要当前用户具备权限 employee:list
        //现在数据库中,只有zs可以访问这个权限,ls没有
        //http.authorizeRequests().antMatchers("employee/list").hasAnyAuthority("employee:list")
    }

    //记住我的功能实现
    @Autowired
    private DataSource dataSource;
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        //这个是用来操作数据库的 实现类
        JdbcTokenRepositoryImpl obj = new JdbcTokenRepositoryImpl();
        obj.setDataSource(dataSource);
        //是否在启动程序时,就自动创建表
        obj.setCreateTableOnStartup(true);
        //启动创建表persistent_logs表,存token,username时会用到
        return obj;
    }
}
  • http.csrf().disable() :屏蔽跨域伪造检查
  • antMatchers("/login.html").permitAll() : 对登录页面跳转路径放行
  • loginPage("/login.html") :登录页面跳转地址(必须)
  • loginProcessingUrl("/login") :登录处理地址(必须)

自定义登录页面

  • 准备登录页面static/login.html  (下面自己随便写的有点丑,前端样式自调)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>这是自定义的登陆界面</h1>
<h2>注意:当自定义登录界面后,传入的帐号和密码的输入框name的名称不能随便写,只能是 username和password</h2>
<form method="post" action="/login">
    <div>
        用户名:<input type="text" name="username">
    </div>
    <div>
        密码:<input type="password" name="password">
    </div>
    <div class="checkbox"><!-- rememberme的name的名称使用的也是默认的,不能修改 -->
        <label><input type="checkbox" id="rememberme" name="remember-me"/>记住我</label>
    </div>
    <div>
        <button type="submit">立即登陆</button>
    </div>
</form>
</body>
</html>

定义UserDetailsService

相关概念

  • UserDetailsService

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

  • UserDetails

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

基于入门案例进行修改

  1. 准备好认证表t_login(密码密文) ,集成MyBatis等,做好准备工作
  1. 创建类UserDetailServiceImpl实现UserDetailsService接口

用户的认证通过Provider来完成,而Provider会通过UserDetailService拿到数据库(或 内存)中的认证信息然后和客户端提交的认证信息做校验。虽然叫Service,但是我更愿 意把它认为是我们系统里经常有的UserDao。

注意:这里定义了UserDetailSerice后,WebSecurityConfig中不在需要定义UserDetailService的Bean需要移除

import java.util.ArrayList;
import java.util.List;

@Service
public class UserDetailServiceImpl implements UserDetailsService {
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private VipUserMapper vipUserMapper;
    /**
     * 加载数据库中的认证的用户的信息:用户名,密码,用户的权限列表
     * @param username: 该方法把username传入进来,我们通过username查询用户的信息
    (密码,权限列表等)然后封装成 UserDetails进行返回 ,交给security 。
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        VipUser vipUser = vipUserMapper.findUserByUserName(username);
        List<GrantedAuthority> authorities = new ArrayList<>();
        List<Permission> permissions= permissionMapper.findPermissionListByUsernme(username);
        for (Permission permission:permissions){
            //将当前用户的权限列表查出来,并保存到security
            //"ROLE_" 使用@Secured注解 ,必须加ROLE_前缀
            //authorities.add(new SimpleGrantedAuthority("ROLE_"+permission.getExpression()));
            authorities.add(new SimpleGrantedAuthority(permission.getExpression()));
        }
        //密码是基于BCryptPasswordEncoder加密的密文
        //User是security内部的对象,UserDetails的实现类 ,
        //用来封装用户的基本信息(用户名,密码,权限列表)
        return new User(username,vipUser.getPassword(),authorities);
    }
}
  1. 编写Mapper.xml
    1. <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      <mapper namespace="cn.zxq.mapper.PermissionMapper">
          <select id="findPermissionListByUsernme" resultType="cn.zxq.domain.Permission">
              SELECT * FROM t_permission WHERE id IN (
                  SELECT permission_id FROM t_role_permission WHERE role_id IN (
                      SELECT role_id FROM t_user_role WHERE user_id IN (
                          SELECT id FROM `hrm-security`.t_vip_user WHERE username =#{username}
                      )
                  )
              )
          </select>
      </mapper>
      

  2. 编写Mapper映射器接口
    1. public interface PermissionMapper extends BaseMapper<Permission> {
      
          List<Permission> findPermissionListByUsernme(String username);
      }

定义密码编码器

在我们的案例中,密码一值是明文的,我们指定的密码编码器是 NoOpPasswordEncoder ,这个是不加密的,但是在生产环境中我们数据库中的密码肯定是密文,所以我们需要指定密码的编码器,那么SpringSecurity在认证时会调用我们指定的密码编码器进行认证

  • BCryptPasswordEncoder

BCryptPasswordEncoder是SpringSecurity内部提供的编码器,他的好处在于多次对相 同的明文加密出来的密文是不一致的,但是多次加密出来的不同密文确有能检查通过

密码最终校验的地方是由Security框架完成。 原始密码保存在SecurityContext上下文,传入一个加密之后的密码:vipUser.getPassword()

Web授权

  1. web授权API说明

在Security配置类中,可以通过HttpSecurity.authorizeRequests()给资源指定访问的权限,其API如下:

  • anyRequest():任何请求
  • antMatchers(“/path”) :匹配某个资源路径
  • authenticationed() : 保护URL需要登录访问anyRequest().authenticationed()
  • permitAll():指定url无需保护(放行)一般用户静态资源antMatchers(“/path”).permitAll()
  • hasRole(String role):某个资源需要用户拥有什么样的role才能访问

antMatchers(“/path”).hasRole(admin)

  • hasAuthority(String authority):某个资源需要用户拥有什么样的权限才能访问

antMatchers(“/path”).hasAuthority(admin)

  • hasAnyRole(String ...roles):某个资源拥有指定角色中的一个就能访问
  • hasAnyAuthority(String ... authorities):某个资源拥有指定权限中的一个就能访问
  • access(String attribute):该方法使用SPEL表达式,可以创建复杂的限制
  • hasIpAddress(String ip):拥有什么样的ip或子网可以访问该资源

授权规则注意

我们通常把细节的规则设置在前面,范围比较大的规则设置放在后面,返例:如有以下配置

.antMatchers("/admin/**").hasAuthority(“admin”)

.antMatchers("/admin/login").permitAll();

那么第二个权限规则将不起作用,因为第一个权限规则覆盖了第二个权限规则

Web授权实战

我们这一次在入门案例的基础上进行修改,所有的认证数据,授权数据都从数据库进行获取

  • 准备数据库和Domain,mapper等

t_user //用户登录表

t_user_role //用户和角色中间表

t_permisstion //权限表

t_role //角色表

t_role_permission //角色和权限中间表

  • 编写controller
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EmployeeController {

    @RequestMapping("/employee/list")
    //@Secured("ROLE_employee:list")
    @PreAuthorize("hasAuthority('employee:list')")
    public String list(){
        return "employee.list";
    }
    @RequestMapping("/employee/add")
    @PreAuthorize("hasAuthority('employee:add')")
    public String add(){
        return "employee.add";
    }
    @RequestMapping("/employee/update")
    @PreAuthorize("hasAnyAuthority('employee:update','dept:update')")
    public String update(){
        return "employee.update";
    }
    @RequestMapping("/employee/delete")
    public String delete(){
        return "employee.delete";
    }
}
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DeptController {

    @RequestMapping("/dept/list")
    public String list(){
        return "dept.list";
    }

    @RequestMapping("/dept/add")
    public String add(){
        return "dept.add";
    }

    @RequestMapping("/dept/update")
    public String update(){
        return "dept.update";
    }

    @RequestMapping("/dept/delete")
    public String delete(){
        return "dept.delete";
    }

}

方法授权

SpringSecurity提供了一些授权的注解让我们可以在service,controller等的方法上贴注解进行授权,即在方法上指定方法方法需要什么样的权限才能访问

@Secured   (不常用)

标记方法需要有什么样的权限才能访问,这个注解需要在配置类上开启授权注解支持;@EnableGlobalMethodSecurity(securedEnabled=true) ,然后在Controller方法上贴该注解如:

@Secured(“IS_AUTHENTICATED_ANONYMOUSLY”) :方法可以匿名访问

@Secured(“ROLE_DEPT”) ,需要拥有部门的角色才能访问,ROLE_前缀是固定的

解释:上图使用了 @Secured("ROLE_employee:list") 意思是 "/employee/list" 这个资源需要“ROLE_employee:list”权限才能访问,如果认证的用户有该权限(UserDetailService中加载)包含了“ROLE_employee:list”即可访问该资源,否则不能访问。

注意:对于方法授权,没有贴注解的方法默认是匿名访问。@Secured注解授权是需要加上前缀“ROLE_”

@PreAuthorize  (推荐使用)

PreAuthorize适合进入方法前的权限验证,拥有和Secured同样的功能,甚至更强大,该注解需要在配置类开启:@EanbleGlobalMethodSecurity(prePostEnabled=true) 方法授权支持,然后在Controller贴注解如下:

@PreAuthorize(“isAnonymous()”) : 方法匿名访问

@PreAuthorize(“hasAnyAuthority(‘p_user_list’,‘p_dept_list’)”) :拥有p_user_listr或者p_dept_list的权限能访问

@PreAuthorize(“hasAuthority(‘p_transfer’) and hasAuthority(‘p_read_accout’)”) : 拥有p_transfer权限和p_read_accout权限才能访问.

该标签不需要有固定的前缀

注意格式“@PreAuthorize("hasAuthority('employee:add')")” ,hasAuthority不能省略,括号中是单引号。

认证成功、失败处理 

  1. 解决方案

自定义类实现AuthenticationSuccessHandler接口复写 onAuthenticationSuccess方法,该方法其中一个参数是Authentication ,他里面封装了认证信息,用户信息UserDetails等,我们需要在这个方法中使用Response写出json数据即可

    1. 认证成功结果处理
  1. 定义AuthenticationSuccessHandler

定义类实现AuthenticationSuccessHandler接口复写onAuthenticationSuccess方法,实现自己的认证成功结果处理

     失败也是一样的

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        Map map = new HashMap<>();
        map.put("success",false);
        map.put("message","认证失败");
        //在返回前端数据时,还可以设置一个http的状态(类似:404、403等)
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-type", "text/html; charset=utf-8");
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.getWriter().print(JSON.toJSONString(map));
        response.getWriter().flush();
        response.getWriter().close();
    }
}
//这个实现类的作用:当security认证成功后,将会执行当前handler处理器
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {
        //这里经常做的事情:当认证成功后,拼接一个JSON格式的数据返回给请求前端
        Map map = new HashMap<>();
        map.put("success",true);
        map.put("message","认证成功");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-type", "text/html; charset=utf-8");
        response.getWriter().print(JSON.toJSONString(map));

        response.getWriter().flush();
        response.getWriter().close();
    }
}

授权失败处理

1.1.概述

当用户请求资源服务的资源时,需要进行用户的认证和授权检查,当认证或授权检查失败,我们需要要返回自己的失败结果信息,可以通过HttpSecurity设置授权失败结果处理器,内部通过 ExceptionTranslationFilter 调用AuthenticationEntryPoint实现匿名用户授权失败结果处理, ExceptionTranslationFilter 通过 AccessDeniedHandler来处理授权失败结果处理。

1.2.定义认证检查失败处理

  1. 定义AccessDeniedHandler

AccessDeineHandler 用来解决认证过的用户访问无权限资源时的异常

public class DefaultAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        String result = JSON.toJSONString(AjaxResult.me().setSuccess(false).setMessage("无访问权限"));
        response.setContentType("text/html;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(result);
        writer.flush();
        writer.close();
    }
}

记住我概述

1.什么是记住我

Remember me(记住我)记住我,当用户发起登录勾选了记住我,在一定的时间内再次登录就不用输入用户名和密码了,即使浏览器退出重新打开也是如此。

2.流程分析

在SpringSecurity中提供RememberMeAuthenticationFilter过滤器来实现记住我功能,其核心流程如下:

  • 认证成功UsernamePasswordAuthenticationFilter会调用RememberMeServices创建Token

(见其父类AbstractAuthenticationProcessingFilter.successfulAuthentication),同时 RemeberMeService 会调用TokenRepository将Token写入数据库(`persistent_logins`),然后 RemeberMeService通 过Reponse.addCookie把Token写到浏览器的Cookies中

  • 当浏览器再次发起请求会进入RemeberMeAuthenticationFilter,该Filter获取到请求cookies中的token交给RemeberMeService
  • RemeberMeService调用TokenRepository去数据库中根据Token查询用户名
  • 调用UserDetilasService.loadUserByUsername根据用户名获取用户认证信息
  • 通过authenticationManager.authenticate,做一次认证,然后把用户信息放入上下文对象中SecurityContext
    //记住我的功能实现
    @Autowired
    private DataSource dataSource;
    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        //这个是用来操作数据库的 实现类
        JdbcTokenRepositoryImpl obj = new JdbcTokenRepositoryImpl();
        obj.setDataSource(dataSource);
        //是否在启动程序时,就自动创建表
        obj.setCreateTableOnStartup(true);
        //启动创建表persistent_logs表,存token,username时会用到
        return obj;
    }
配置记住我功能 ,参数1:3600单位是秒;参数2:userDetailsService 自定义的userDetailServiceImpl
http.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(3600)  //过期时间
        .userDetailsService(userDetailsService); //用来加载用户认证信息的

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值