Spring securty<七> 认证–匿名用户拦截器源码分析
文章目录
- Spring securty<七> 认证--匿名用户拦截器源码分析
- 1、简介
- 2、特性
- 2.1、身份验证
- 2.2、资源保护,防止跨站点请求伪造(CSRF)
- 3、鉴权说明
- 4、匿名用户拦截器源码分析
- 4.1、源码明细
- 4.2、构造方法
- 4.3、拦截器的执行方法
- 5、SessionManagementFilter、ExceptionTranslationFilter
- 5.1、SessionManagementFilter 会话管理的拦截器
- 5.2、ExceptionTranslationFilter 异常处理的拦截器
本地项目的基础环境
环境 | 版本 |
jdk | 1.8.0_201 |
maven | 3.6.0 |
Spring-boot | 2.3.3.RELEASE |
1、简介
spring security是一个提供身份验证、授权和防止常见攻击的框架,它对命令式和反应式应用程序都有一流的支持,是保护基于Spring的应用程序的事实上的标准。
2、特性
2.1、身份验证
springsecurity作为身份验证,身份验证是验证试图访问特定资源的用户的身份的方法。对用户进行身份验证的常见方法是要求用户输入用户名和密码。一旦执行身份验证,我们就知道身份并可以执行授权。
2.2、资源保护,防止跨站点请求伪造(CSRF)
springsecurity提供了针对常见漏洞利用的保护。只要可能,默认情况下都会启用保护。
3、鉴权说明
前面的文章中,我们一直在说的是认证这块的事,安全框架上,认证是一个比较复杂的事,认证也是基于拦截器链,来实现的;鉴权当然也一样,也是基于拦截器链实现的;
下面的图中,是基于上一篇的项目badger-spring-security-5
来的;
当上述的认证的拦截器,没有异常的时候,就会走到上图中,下标为8的,匿名用户的拦截器;也就是鉴权的开始;这篇文章,也是主要讲解下这块的源码;
4、匿名用户拦截器源码分析
4.1、源码明细
package org.springframework.security.web.authentication;
import java.io.IOException;
import java.util.*;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
public class AnonymousAuthenticationFilter extends GenericFilterBean implements
InitializingBean {
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
private String key;
private Object principal;
private List<GrantedAuthority> authorities;
//构造方法
public AnonymousAuthenticationFilter(String key) {
this(key, "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
}
//构造方法
public AnonymousAuthenticationFilter(String key, Object principal,
List<GrantedAuthority> authorities) {
Assert.hasLength(key, "key cannot be null or empty");
Assert.notNull(principal, "Anonymous authentication principal must be set");
Assert.notNull(authorities, "Anonymous authorities must be set");
this.key = key;
this.principal = principal;
this.authorities = authorities;
}
@Override
public void afterPropertiesSet() {
Assert.hasLength(key, "key must have length");
Assert.notNull(principal, "Anonymous authentication principal must be set");
Assert.notNull(authorities, "Anonymous authorities must be set");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) req));
if (logger.isDebugEnabled()) {
logger.debug("Populated SecurityContextHolder with anonymous token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
chain.doFilter(req, res);
}
protected Authentication createAuthentication(HttpServletRequest request) {
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
principal, authorities);
auth.setDetails(authenticationDetailsSource.buildDetails(request));
return auth;
}
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource,
"AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public Object getPrincipal() {
return principal;
}
public List<GrantedAuthority> getAuthorities() {
return authorities;
}
}
4.2、构造方法
初始化源码
org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer.class
类中的init方法
private Object principal = "anonymousUser";
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
@Override
public void init(H http) {
if (authenticationProvider == null) {
authenticationProvider = new AnonymousAuthenticationProvider(getKey());
}
if (authenticationFilter == null) {
authenticationFilter = new AnonymousAuthenticationFilter(getKey(), principal,
authorities);
}
authenticationProvider = postProcess(authenticationProvider);
http.authenticationProvider(authenticationProvider);
}
private String getKey() {
if (key == null) {
key = UUID.randomUUID().toString();
}
return key;
}
上述可以看到,有三个参数:
-
key
:启动的时候,默认一个随机uuid; -
principal
:可以理解成用户名anonymousUser
-
authorities
:默认权限ROLE_ANONYMOUS
4.3、拦截器的执行方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
//当前会话中,认证的对象为空
if (SecurityContextHolder.getContext().getAuthentication() == null) {
//把当前会话,认证成匿名用户
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) req));
if (logger.isDebugEnabled()) {
logger.debug("Populated SecurityContextHolder with anonymous token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
chain.doFilter(req, res);
}
protected Authentication createAuthentication(HttpServletRequest request) {
//创建一个匿名用户的token对象
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
principal, authorities);
auth.setDetails(authenticationDetailsSource.buildDetails(request));
return auth;
}
代码比较简单,步骤也是中文注释了;其实就是,当用户未登录的时候,给当前访问者一个匿名用户,权限是ROLE_ANONYMOUS
。
这样,每个用户进来,无论是登录或者未登录的,上下文对象中SecurityContextHolder.getContext()
,都有用户,并且都有权限信息;
匿名用户,在之后的鉴权中,也是有需要和用到的。
5、SessionManagementFilter、ExceptionTranslationFilter
5.1、SessionManagementFilter 会话管理的拦截器
这个拦截器,是管理会话信息的,有兴趣的,自己看下源码就好了,分布式的会话中,可以重写这个,也可以直接在登录成功后,就存入三方组件中(例如:redis);
5.2、ExceptionTranslationFilter 异常处理的拦截器
这个拦截器,自己看源码就可以了,主要是处理全局异常的;