Spring securty<七> 认证--匿名用户拦截器源码分析

Spring securty<七> 认证–匿名用户拦截器源码分析


本地项目的基础环境

环境版本
jdk1.8.0_201
maven3.6.0
Spring-boot2.3.3.RELEASE

1、简介

spring security是一个提供身份验证、授权和防止常见攻击的框架,它对命令式和反应式应用程序都有一流的支持,是保护基于Spring的应用程序的事实上的标准。详细可以参看《spring security官网》

《Spring securty<一> 简介入门案例》

《Spring securty<二> 配置项详解》

《Spring securty<三> 认证案例代码》

《Spring securty<四> 认证的源码解析》

《Spring securty<五> 认证–帐号/邮箱/手机号+密码》

《Spring securty<六> 认证–手机号+验证码》

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 异常处理的拦截器

这个拦截器,自己看源码就可以了,主要是处理全局异常的;

详细的代码,可以查看《码云》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

葵花下的獾

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

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

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

打赏作者

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

抵扣说明:

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

余额充值