1. WebMvcSecurityConfiguration
- 在SpringSecurtiy中有一些参数是可以在SpringMVC中直接进行自动装配的,就像下面这三个
![[SpringSecurity5.6.2源码分析二十八]:WebMvcSecurityConfiguration](https://static.blogweb.cn/article/dabca5751d6b4844801cf948cf7b266c.webp)
- 而这些能够起作用正是因为WebMvcSecurityConfiguration注册了这三个参数对应的参数解析器
- 下面是此配置类的导入链路和源码
![[SpringSecurity5.6.2源码分析二十八]:WebMvcSecurityConfiguration](https://static.blogweb.cn/article/bd3790adfb26460ab57c8e5ac907c12e.webp)
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
private BeanResolver beanResolver;
@Override
@SuppressWarnings("deprecation")
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
authenticationPrincipalResolver.setBeanResolver(this.beanResolver);
argumentResolvers.add(authenticationPrincipalResolver);
argumentResolvers
.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
CurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();
currentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);
argumentResolvers.add(currentSecurityContextArgumentResolver);
// 注册 CsrfToken 的参数解析器
argumentResolvers.add(new CsrfTokenArgumentResolver());
}
@Bean
RequestDataValueProcessor requestDataValueProcessor() {
return new CsrfRequestDataValueProcessor();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanResolver = new BeanFactoryResolver(applicationContext.getAutowireCapableBeanFactory());
}
}
- 通过分析源码我们可以知道这里导入了四个参数解析器,但是实际上是三种不同类型的参数解析器
- AuthenticationPrincipalArgumentResolver
- CurrentSecurityContextArgumentResolver
- CsrfTokenArgumentResolver
2. SpringSecurity中是如何注册参数解析器
- 要讲注册的这些参数解析器是如何工作的得先介绍SpringMVC是如何识别到SpringSecurity注册的参数解析的
- 先看下图,这是SpringMVC的自动配置类
![[SpringSecurity5.6.2源码分析二十八]:WebMvcSecurityConfiguration](https://static.blogweb.cn/article/3f6ca862946748208729c27f7bac6d87.webp)
- 而在EnableWebMvcConfiguration中有一个addArgumentResolvers(...)方法,可以看到这里就有我们注册的WebMvcSecurityConfiguration了
![[SpringSecurity5.6.2源码分析二十八]:WebMvcSecurityConfiguration](https://static.blogweb.cn/article/644f97a6bd4a49148e9ac1af098a9230.webp)
- 然后我们再看下面这个方法,这里很明显是将容器中的WebMvcConfigurer注册到当前类中
![[SpringSecurity5.6.2源码分析二十八]:WebMvcSecurityConfiguration](https://static.blogweb.cn/article/79ee25dd93b94540b43633a6cefb11b5.webp)
- 所以说WebMvcSecurityConfiguration才实现了WebMvcConfigurer
![[SpringSecurity5.6.2源码分析二十八]:WebMvcSecurityConfiguration](https://static.blogweb.cn/article/2569c4fdc1d54e4793e092ad099dcb0b.webp)
3. AuthenticationPrincipalArgumentResolver
- SpringSecurity中注册了两个名称一样但是包路径不一样的参数解析器,但是这两个参数解析器的作用是大抵相同的
- 任何参数解析器都是由下面两大核心销方法组成的
public interface HandlerMethodArgumentResolver {
/**
* 判断此参数解析器是否支持此参数
*/
boolean supportsParameter(MethodParameter parameter);
/**
* 解析入参,并返回参数值
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
- 然后我们再看AuthenticationPrincipalArgumentResolver
public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
...
@Override
public boolean supportsParameter(MethodParameter parameter) {
return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
// 通过线程级别的安全上下文获取参数
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
}
Object principal = authentication.getPrincipal();
AuthenticationPrincipal annotation = findMethodAnnotation(AuthenticationPrincipal.class, parameter);
String expressionToParse = annotation.expression();
// 解析表达式值
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(principal);
context.setVariable("this", principal);
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse);
principal = expression.getValue(context);
}
if (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) {
if (annotation.errorOnInvalidType()) {
throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
}
return null;
}
return principal;
}
...
}
- 分析源码可知:
- 此参数解析器只支持@AuthenticationPrincipal
- @AuthenticationPrincipal可以支持自动装配认证对象中的主体(用户对象),以及可以通过SpelExpressionParser自动装配用户对象中的属性
4. CurrentSecurityContextArgumentResolver
- CurrentSecurityContextArgumentResolver:
- 支持Controller中的方法中的入参中有标注了@CurrentSecurityContext放在SecurityContext参数上
- 支持SpringSpEl表达式从SecurityContext中获取值
- eg:@CurrentSecurityContext(expression="authentication") Authentication authentication
public final class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
...
/**
* 此参数解析器只能支持带有 {@code CurrentSecurityContext} 注解的参数
* @param parameter
* @return
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
// 从线程级别的策略中拿到安全上下文
SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext == null) {
return null;
}
Object securityContextResult = securityContext;
// 从参数上拿到指定的 CurrentSecurityContext 注解信息
CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
String expressionToParse = annotation.expression();
// 是否以 SpEL 进行解析
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(securityContext);
context.setVariable("this", securityContext);
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse);
securityContextResult = expression.getValue(context);
}
// 如果有安全上下文,但是参数类型不对
if (securityContextResult != null
&& !parameter.getParameterType().isAssignableFrom(securityContextResult.getClass())) {
// 是否抛出异常,还是返回空
if (annotation.errorOnInvalidType()) {
throw new ClassCastException(
securityContextResult + " is not assignable to " + parameter.getParameterType());
}
return null;
}
return securityContextResult;
}
...
}
5. CsrfTokenArgumentResolver
- CsrfTokenArgumentResolver:为了解析方法入参中有CsrfToken的参数解析器
public final class CsrfTokenArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 此参数解析器仅支持 {@code CsrfToken}
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return CsrfToken.class.equals(parameter.getParameterType());
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
// 从请求域中获得CsrfToken, 此属性值是由CsrfFilter负责放入的
CsrfToken token = (CsrfToken) webRequest.getAttribute(CsrfToken.class.getName(),
RequestAttributes.SCOPE_REQUEST);
return token;
}
}