初始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
- 通过配置类
- 自定义编写实现类
- 创建配置类,设置使用哪个userDetailsService实现类
- 编写实现类,返回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
}
}
查询数据库完成认证
- 引入相关依赖
<?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>
- 创建数据库表
- 创建实体
@Data
public class User {
private String id;
private String username;
private String password;
}
- 整合MybatisPlus
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sparrow.security.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
- 在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
}
}
- 在启动类上添加Mapper扫描注解
@SpringBootApplication
@MapperScan("com.sparrow.security.mapper")
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
- 数据库信息添加到配置文件
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
自定义用户登录页面
- 重写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();
}
- 创建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。
- 在配置类中设置目标路径需要的权限
// 使用自定义认证页面
@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防护
}
- 在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防护
}
- 在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_"。
- 启动类(或配置类)开启注解
@SpringBootApplication
@MapperScan("com.sparrow.security.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class, args);
}
}
- 在controller的方法上面使用注解来配置角色
@GetMapping("/testAnnotation")
@Secured({"ROLE_normal","ROLE_admin"})
public String testAnnotation(){
return "hello Annotation";
}
@PreAuthorize |
---|
进入方法前的权限验证,可以将登录用户的roles/permissions参数传到方法中。
- 启动类(或配置类)开启注解
@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);
}
}
- 在controller的方法上面使用注解来配置角色
@GetMapping("/testAnnotations")
@PreAuthorize("hasAnyAuthority('admins')")
public String testAnnotations(){
return "hello Annotations";
}
@PostAuthorize |
---|
使用不多,在方法执行后再进行权限验证,适合验证带有返回值的权限。
- 启动类(或配置类)开启注解
@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);
}
}
- 在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";
}
用户注销
- 在配置类中添加退出配置
//用户注销
http.logout().logoutUrl("/logout") //退出登录访问url
.logoutSuccessUrl("/login.html") //登出后跳转页面
.permitAll();
---------------------------------------可复制案例--------------------------------------
自动登录(记住我功能实现)
-
实现原理
-
具体实现
- 创建数据库表
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
- 配置类注入数据源,配置操作数据库对象
//注入数据源
@Autowired
private DataSource dataSource;
//配置对象
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// 是否自动生成数据库表
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
- 配置类中配置自动登录
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防护
}
- 登录也增加自动登录复选框
<!--这里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可能会比较常见,即密码错误的时候。
组件比较多,但是如果主要流程理顺了,也比较简单。