SpringSecurity--learning

初始Spring Security

SpringSecurity概念
Spring Security是spring采用AOP思想,基于servlet过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了Spring IoC,DI(控制反转 Inversion of Control ,DI:Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

SpringSecurity VS Shiro

SpringSecurity

  • 和spring无缝整合
  • 全面的权限控制
  • 专门为Web开发而设计(旧版本不能脱离Web环境,新版本对整个框架进行了分层抽取,分成了核心 模块和Web模块。单独引入核心模块就可以脱离Web环境)
  • 重量级

Shiro

  • Apache旗下的轻量级权限控制框架
  • 轻量级。Shiro主张的理念是把复杂的事情变简单。针对性能有更高要求的互联网应用有更好表现。
  • 通用性。(好处:不局限与Web环境,可以脱离Web环境使用。缺陷:在Web环境下一些特定的需求需要手动编写代码定制)

总结
  SpringSecurity是Spring家族中的一个安全管理框架,实际上,在SpringBoot出现之前,SpringSecurity就已经发展了多年了,但是使用的并不多,安全管理领域,一直是Shiro的天下。
  相对于Shiro在SSM中整合SpringSecurity都是比较麻烦的操作,所以SpringSecurity虽然功能比Shiro强大,但是使用反而没有Shiro多(Shiro虽然功能没有SpringSecurity多,但是对于大部分项目而言,Shiro也够用了)。
  自从有了SpringBoot之后,SpringBoot对于SpringSecurity提供了自动化配置方案,可以使用更少的配置来使用SpringSecurity。
  因此一般推荐搭配:
  SSM + Shiro
  SpingBoot/SpringCloud + SpringSecurity

Spring Security - Hello World

  • springboot项目引入springsecurity依赖后,springboot自动会对springsecurity进行配置,进行管控。
    在这里插入图片描述
    默认账号为:user,密码为springboot项目启动时控制台输出的
    在这里插入图片描述

Spring Security基本原理

本质上是一个过滤器链。每一个过滤器都通过才可以执行下去。
过滤器加载过程:简单来说就是先进行初始化过滤器链对象-》遍历过滤器容器数组,将过滤器配置到过滤器链对象。
在这里插入图片描述

Spring Security开发过程中2个重要的接口

UserDetailsService

当什么也没有配置的时候,账号和密码是由SpringSecurity定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过自定义逻辑控制认证逻辑。
  这个接口的作用就是用来描述查询数据库用户名和密码的过程。
  使用方式:
     *创建类继承UsernamePasswordAuthenticationFilter,重写三个方法
      * 创建类实现UserDetailService,查询数据,返回User对象,这个User对象是框架提供的。

PasswordEncoder

数据加密接口,用于返回User对象里面的密码加密。

Web开发中的权限方案

先认证再授权
配置用户名密码的三种方式:

  • 通过配置文件
server:
  port: 8080
spring:
  security:
    user:
      name: root
      password: root
  • 通过配置类
  • 自定义编写实现类
  1. 创建配置类,设置使用哪个userDetailsService实现类
  2. 编写实现类,返回User对象,User对象有用户名密码的操作权限
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailService userDetailService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService).passwordEncoder(password());
    }

    @Bean
    PasswordEncoder password(){
        return new BCryptPasswordEncoder();
    }
}
@Service
public class MyUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");  //工具类临时添加角色
        return new User("Tom",new BCryptPasswordEncoder().encode("123"),auths); //这个User对象必须是SpringSecurity提供的User
    }
}

查询数据库完成认证

  1. 引入相关依赖
<?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 https://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.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sparrow</groupId>
    <artifactId>security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>security</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <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>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0-RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  1. 创建数据库表在这里插入图片描述
  2. 创建实体
@Data
public class User {
    private String id;

    private String username;

    private String password;
}
  1. 整合MybatisPlus
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sparrow.security.entity.User;

public interface UserMapper extends BaseMapper<User> {
}

  1. 在MyUserDetailsService调用mapper里面的方法查询数据库进行用户认证
@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 查询
        QueryWrapper<com.sparrow.security.entity.User> wrapper = new QueryWrapper();
        wrapper.eq("username",username);
        com.sparrow.security.entity.User user = userMapper.selectOne(wrapper);
		// 判断
        if (user != null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");  //工具类临时添加角色
        return new User(user.getUsername(),new BCryptPasswordEncoder().encode(user.getPassword()),auths); //这个User对象必须是SpringSecurity提供的User
    }
}
  1. 在启动类上添加Mapper扫描注解
@SpringBootApplication
@MapperScan("com.sparrow.security.mapper")
public class SecurityApplication {

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

}

  1. 数据库信息添加到配置文件
server:
  port: 8080
spring:
  security:
    user:
      name: root
      password: root
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
    username: root
    password: root

自定义用户登录页面

  1. 重写configure(HttpSecurity http)方法
@Configuration                                                                                                                           
public class SecurityConfig extends WebSecurityConfigurerAdapter {                                                                       
    @Autowired                                                                                                                           
    private MyUserDetailService userDetailService;                                                                                       
                                                                                                                                         
    // 使用spring security自带的认证页面                                                                                                          
    @Override                                                                                                                            
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {                                                       
        auth.userDetailsService(userDetailService).passwordEncoder(password());                                                          
    }                                                                                                                                    
                                                                                                                                         
    // 使用自定义认证页面                                                                                                                         
    @Override                                                                                                                            
    protected void configure(HttpSecurity http) throws Exception {                                                                       
        http.formLogin()    //自定义自己编写的登录页面                                                                                               
            .loginPage("/login.html")  // 设置登录页面                                                                                         
            .loginProcessingUrl("/user/login")  //登录访问路径                                                                                 
            .defaultSuccessUrl("/test/index").permitAll()  //登录成功后,跳转路径                                                                  
            .and().authorizeRequests().antMatchers("/","/test/hello","/user/login").permitAll()  //设置可以直接访问的路径,不需要认证                     
            .anyRequest().authenticated()      // 所有请求都可以访问                                                                              
            .and().csrf().disable(); //关闭csrf防护                                                                                          
    }                                                                                                                                    
                                                                                                                                         
    @Bean                                                                                                                                
    PasswordEncoder password(){                                                                                                          
        return new BCryptPasswordEncoder();                                                                                              
    }                                                                                                                                    
  1. 创建login.html
    在这里插入图片描述
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>title</title>
    </head>
    <body>
        <form action="/user/login" method="post">
            <!--用户名必须为username,密码必须为password,否则springsecurity取不到-->
            <!--原因:由于springsecurity获取用户名密码的是在UsernamePasswordAuthenticationFilter.class
                     中获取的,这个过滤器内容有个方法attemptAuthentication方法,这个方法首先判断post请求,
                     然后通过哦不太onUsername()和obtaionPassword()这两个方法来获取账号密码,这两个方
                     法是通过request.getParameter()方法;来获取的,其中的参数写死为username和password-->
            用户名:<input type="text" name="username"/>
            <br/>
            密 码:<input type="text" name="password"/>
            <br/>
            <input type="submit" value="login">
        </form>
    </body>
</html>

基于角色或权限进行访问控制

hasAuthority方法

如果当前的主体具有指定的权限,则返回true,否则返回false。

  1. 在配置类中设置目标路径需要的权限
// 使用自定义认证页面                                                                                                                        
@Override                                                                                                                           
protected void configure(HttpSecurity http) throws Exception {                                                                      
    http.formLogin()    //自定义自己编写的登录页面                                                                                              
        .loginPage("/login.html")  // 设置登录页面                                                                                        
        .loginProcessingUrl("/user/login")  //登录访问路径                                                                                
        .defaultSuccessUrl("/test/index").permitAll()  //登录成功后,跳转路径                                                                 
        .and().authorizeRequests().antMatchers("/","/test/hello","/user/login").permitAll()  //设置可以直接访问的路径,不需要认证                    
         //当前登录用户,只有具有admins权限才能访问目标路径                                                                                              
        .antMatchers("/test/index").hasAuthority("admins")                                                                          
        .and().csrf().disable(); //关闭csrf防护                                                                                         
}                                                                                                                                   
  1. 在UserDetailsService中给User对象设置权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");  //工具类临时添加角色
hasAnyAuthority方法

如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true。
3. 在配置类中设置目标路径需要的权限

// 使用自定义认证页面                                                                                                                          
@Override                                                                                                                             
protected void configure(HttpSecurity http) throws Exception {                                                                        
    http.formLogin()    //自定义自己编写的登录页面                                                                                                
        .loginPage("/login.html")  // 设置登录页面                                                                                          
        .loginProcessingUrl("/user/login")  //登录访问路径                                                                                  
        .defaultSuccessUrl("/test/index").permitAll()  //登录成功后,跳转路径                                                                   
        .and().authorizeRequests().antMatchers("/","/test/hello","/user/login").permitAll()  //设置可以直接访问的路径,不需要认证                      
         //当前登录用户,只有具有admins权限才能访问目标路径                                                                                                
        .antMatchers("/test/index").hasAuthority("admins")                                                                            
         //当前登录用户,具有admins,manager任意权限就能访问目标路径                                                                                        
        .antMatchers("/test/find").hasAnyAuthority("admins,manager")                                                                  
        .and().csrf().disable(); //关闭csrf防护                                                                                           
}                                                                                                                                     
  1. 在UserDetailsService中给User对象设置权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("manager");  //工具类临时添加角色
hasRole方法

如果用户具备给定角色就允许访问,否则出现403。如果当前主体具有指定角色,则返回true。

 //由于SpringSecurity底层在源码中角色前加了'ROLE_',所以在定义角色时候需要加上'ROLE_'
            .antMatchers("/test/find").hasRole("sale")

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("manager,ROLE_sale");  
hasAnyRole方法

如果用户具备给定角色就允许访问,否则出现403。如果当前主体具有指定角色,则返回true。

 //由于SpringSecurity底层在源码中角色前加了'ROLE_',所以在定义角色时候需要加上'ROLE_'
            .antMatchers("/test/find").hasAnyRole("sale,admin")

List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_admin,ROLE_sale");  

自定义403权限拦截页面

配置类中做下配置就OK了

  //配置没有权限时候403访问的自定义页面
        http.exceptionHandling().accessDeniedPage("/403.html");

注解的使用

@Secured
用户是否具有某个角色,另外需要注意的是这里匹配的字符串需要添加前缀"ROLE_"。
  1. 启动类(或配置类)开启注解
@SpringBootApplication
@MapperScan("com.sparrow.security.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityApplication {

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

}
  1. 在controller的方法上面使用注解来配置角色
@GetMapping("/testAnnotation")
    @Secured({"ROLE_normal","ROLE_admin"})
    public String testAnnotation(){
       return "hello Annotation";
    }
@PreAuthorize

进入方法前的权限验证,可以将登录用户的roles/permissions参数传到方法中。

  1. 启动类(或配置类)开启注解
@SpringBootApplication
@MapperScan("com.sparrow.security.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SecurityApplication {

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

}
  1. 在controller的方法上面使用注解来配置角色
 	@GetMapping("/testAnnotations")
    @PreAuthorize("hasAnyAuthority('admins')")
    public String testAnnotations(){
        return "hello Annotations";
    }
@PostAuthorize

使用不多,在方法执行后再进行权限验证,适合验证带有返回值的权限。

  1. 启动类(或配置类)开启注解
@SpringBootApplication
@MapperScan("com.sparrow.security.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SecurityApplication {

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

}
  1. 在controller的方法上面使用注解来配置角色
 	 @GetMapping("/testAnnotations")
     @PostAuthorize("hasAnyAuthority('admins')")
    public String testAnnotations(){
        return "hello Annotations";
    }
@PreFilter

对传入方法的数据进行过滤

 	@PostMapping("/testAnnotations")
    @PostAuthorize("hasAnyAuthority('admins,sale')")
    @PreFilter("filterObject.name == 'tom'")
    public String testAnnotations(){
        return "hello Annotations";
    }
@PostFilter

对方法返回的数据进行的行过滤

 	@PostMapping("/testAnnotations")
    @PostAuthorize("hasAnyAuthority('admins,sale')")
    @PostFilter("filterObject.name == 'tom'")
    public String testAnnotations(){
        return "hello Annotations";
    }

用户注销

  1. 在配置类中添加退出配置
		//用户注销
        http.logout().logoutUrl("/logout")   //退出登录访问url
            .logoutSuccessUrl("/login.html") //登出后跳转页面
            .permitAll();

---------------------------------------可复制案例--------------------------------------

自动登录(记住我功能实现)

  • 实现原理
    登录后向缓存中(cookie、redis等)存入加密串,下次请求时候,header中带着加密串,后端进行解密来鉴定是否放行。![在这里插入图片描述](https://img-blog.csdnimg.cn/b9359ba7ea5442b7bc1cb844832adca6.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBATV8t,size_20,color_FFFFFF,t_70,g_se,x_16)

  • 具体实现

  1. 创建数据库表
CREATE TABLE `persistent_logins`  (
		`username` VARCHAR ( 64 ) NOT NULL,
		`series` VARCHAR ( 64 ) NOT NULL,
		`token` VARCHAR ( 64 ) NOT NULL,
		`last_used` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
		PRIMARY KEY ( `series` )
	) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=dynamic
  1. 配置类注入数据源,配置操作数据库对象
    //注入数据源
    @Autowired
    private DataSource dataSource;

    //配置对象
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 是否自动生成数据库表
        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }
  1. 配置类中配置自动登录
 http.formLogin()    //自定义自己编写的登录页面
            .loginPage("/login.html")  // 设置登录页面
            .loginProcessingUrl("/user/login")  //登录访问路径
            .defaultSuccessUrl("/logout.html").permitAll()  //登录成功后,跳转路径
            .and().authorizeRequests().antMatchers("/","/test/hello","/user/login").permitAll()  //设置可以直接访问的路径,不需要认证
             //当前登录用户,只有具有admins权限才能访问目标路径
             //.antMatchers("/test/index").hasAuthority("admins")
             //当前登录用户,具有admins,manager任意权限就能访问目标路径
             //.antMatchers("/test/find").hasAnyAuthority("admins,manager")
             //由于SpringSecurity底层在源码中角色前加了'ROLE_',所以在定义角色时候需要加上'ROLE_'
            .antMatchers("/test/find").hasAnyRole("sale")
             //配置记住我
            .and().rememberMe().tokenRepository(persistentTokenRepository())
             // 设置有效时长,单位s
            .tokenValiditySeconds(20).userDetailsService(userDetailService)
            .and().csrf().disable(); //关闭csrf防护
    }
  1. 登录也增加自动登录复选框
 <!--这里name必须为remeber-me,否则springsecurity识别不了-->
 自动登录:<input type="checkbox" name="remeber-me">

---------------------------------------原理解析-----------------------------------------

spring security常用过滤器链

  • FilterSecurityInterceptor
      是一个方法级的权限过滤器,基本位于过滤链的最底部。
      获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限。

  • ExceptionTranslationFilter
      是个异常过滤器,用来处理再认证过程中抛出的异常。
      异常转换过滤器位于整个spring securityFilterChain的后方,用来转换整个链路中出现的异常。

  • UsernamePasswordAuthenticationFilter
      认证操作的主要过滤器,默认匹配URL为/login且必须为POST请求,内部为登录认证逻辑。

  • SecurityContextPersistenceFilter
      最重要的一个过滤器,主要是使用securityContextRepository在session中保存或更新一个SecurityContext(security域),并将SecurityContext给以后的过滤器使用,来为后续的filter建立所需的上下文,SecurityContext中存储了当前用户的认证以及权限信息。

  • WebAsyncManagerIntegrationFilter
      用于集成securityContext到spring异步执行机制中的WebAsyncManager

  • HeaderWriteFilter
      向请求的Header中添加相应的信息,可在http标签内部使用security:headers来控制(不常用)

  • csrf.CsrfFulter
       csrf又称跨域请求伪造,springsecurity会对所有post请求验证是否包含系统生成的csrf的token信息,如果不包含则报错,起到了防止csrf攻击的效果

  • LogoutFilter
      匹配URL为/logout的请求,实现用户退出,清除认证信息。

  • DefaultLoginPageGeneratingFilter
      如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面。

  • DefaultLogoutPageGeneratingFilter
      默认登出页面

  • BasicAuthenticationFilter
      此过滤器会自动解析HTTP请求中请求头中名字为Authentication且以Basic开头的头信息。

  • RequestCacheAwareFilter
       通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest

  • SecurityContextHolderAwareRequestFilter
       针对ServletRequest进行了一次包装,使得request具有更加丰富的API

  • AnonymousAuthenticationFilter
       当securitycontextHolder中认证信息为空,则会创建一个匿名用户存入SecurityContextHolder中。spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。

  • SessionManagementFilter
      SecurityContextRepository限制同一用户开启多个回话

Spring Security 核心组件

spring security核心组件有:Userdetails 、Authentication,UserDetailsService、AuthenticationProvider、AuthenticationManager 下面分别介绍。

Authentication

authentication 直译过来是“认证”的意思,在Spring Security 中Authentication用来表示当前用户是谁,一般来讲你可以理解为authentication就是一组用户名密码信息。Authentication也是一个接口,其定义如下:

public interface Authentication extends Principal, Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();
	Object getCredentials();
	Object getDetails();
	Object getPrincipal();
	boolean isAuthenticated();
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

接口有4个get方法,分别获取

  • Authorities, 填充的是用户角色信息。

  • Credentials,直译,证书。填充的是密码。

  • Details ,用户信息。

  • Principal 直译,形容词是“主要的,最重要的”,名词是“负责人,资本,本金”。感觉很别扭,所以,还是不翻译了,直接用原词principal来表示这个概念,其填充的是用户名。因此可以推断其实现类有这4个属性。

这几个方法作用如下:

  • getAuthorities: 获取用户权限,一般情况下获取到的是用户的角色信息。

  • getCredentials: 获取证明用户认证的信息,通常情况下获取到的是密码等信息。

  • getDetails: 获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)

  • getPrincipal: 获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetails (UserDetails也是一个接口,里边的方法有getUsername,getPassword等)。

  • isAuthenticated: 获取当前 Authentication 是否已认证。

  • setAuthenticated: 设置当前 Authentication 是否已认证(true or false)。

UserDetails

UserDetails,看命知义,是用户信息的意思。其存储的就是用户信息,其定义如下:

public interface UserDetails extends Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();
	String getPassword();
	String getUsername();
	boolean isAccountNonExpired();
	boolean isAccountNonLocked();
	boolean isCredentialsNonExpired();
	boolean isEnabled();
}

方法含义如下:

  • getAuthorites:获取用户权限,本质上是用户的角色信息。

  • getPassword: 获取密码。

  • getUserName: 获取用户名。

  • isAccountNonExpired: 账户是否过期。

  • isAccountNonLocked: 账户是否被锁定。

  • isCredentialsNonExpired: 密码是否过期。

  • isEnabled: 账户是否可用。

UserDetailsService

提到了UserDetails就必须得提到UserDetailsService, UserDetailsService也是一个接口,且只有一个方法loadUserByUsername,他可以用来获取UserDetails。

package org.springframework.security.core.userdetails;

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

通常在spring security应用中,我们会自定义一个CustomUserDetailsService来实现UserDetailsService接口,并实现其public UserDetails loadUserByUsername(final String login);方法。我们在实现loadUserByUsername方法的时候,就可以通过查询数据库(或者是缓存、或者是其他的存储形式)来获取用户信息,然后组装成一个UserDetails,(通常是一个org.springframework.security.core.userdetails.User,它继承自UserDetails) 并返回。

在实现loadUserByUsername方法的时候,如果我们通过查库没有查到相关记录,需要抛出一个异常来告诉spring security来“善后”。这个异常是org.springframework.security.core.userdetails.UsernameNotFoundException。

AuthenticationProvider

负责真正的验证。

当我们使用 authentication-provider 元素来定义一个 AuthenticationProvider 时,如果没有指定对应关联的 AuthenticationProvider 对象,Spring Security 默认会使用 DaoAuthenticationProvider。DaoAuthenticationProvider 在进行认证的时候需要一个 UserDetailsService 来获取用户的信息 UserDetails,其中包括用户名、密码和所拥有的权限等。所以如果我们需要改变认证的方式,我们可以实现自己的 AuthenticationProvider;如果需要改变认证的用户信息来源,我们可以实现 UserDetailsService。

实现了自己的 AuthenticationProvider 之后,我们可以在配置文件中这样配置来使用我们自己的 AuthenticationProvider。其中 myAuthenticationProvider 就是我们自己的 AuthenticationProvider 实现类对应的 bean。

AuthenticationProvider 接口如下:

package org.springframework.security.authentication;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

public interface AuthenticationProvider {
    Authentication authenticate(Authentication var1) throws AuthenticationException;

    boolean supports(Class<?> var1);
}
  • authenticate 表示认证的动作。

  • supports 表示所支持的 Authentication类型。Authentication 包含很多子类,如果 AbstractAuthenticationToken 。

AbstractAuthenticationToken implements Authentication

还有,可以自定义 Authentication ,比如 本实例所使用的: JwtAuthenticationToken。

AuthenticationManager

认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider。AuthenticationManager 中可以定义有多个 AuthenticationProvider。

AuthenticationManager 是一个接口,它只有一个方法,接收参数为Authentication,其定义如下:

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}

AuthenticationManager 的作用就是校验Authentication,如果验证失败会抛出AuthenticationException异常。AuthenticationException是一个抽象类,因此代码逻辑并不能实例化一个AuthenticationException异常并抛出,实际上抛出的异常通常是其实现类,如DisabledException,LockedException,BadCredentialsException等。BadCredentialsException可能会比较常见,即密码错误的时候。

组件比较多,但是如果主要流程理顺了,也比较简单。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

base 西安内推私信

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值