当前位置:   article > 正文

Spring旅程_did not set securitycontextholder since already au

did not set securitycontextholder since already authenticated

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。

SpringBoot 基于 Spring 开发。SpringBoot 本身并不提供 Spring 框架的核心特性以及扩展功能,也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。

  关于 SpringBoot 有一句很出名的话就是约定大于配置。采用 Spring Boot 可以大大的简化开发模式,它集成了大量常用的第三方库配置,所有你想集成的常用框架,它都有对应的组件支持,例如 Redis、MongoDB、Jpa、kafka,Hakira 等等。SpringBoot 应用中这些第三方库几乎可以零配置地开箱即用,大部分的 SpringBoot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。

视频资料

视频专辑-视频合集-哔哩哔哩视频

Java视频合集-哔哩哔哩视频

HTML视频合集

Java

Java视频合集-哔哩哔哩视频

Java进阶之lambda函数式接口

Java中Lambda表达式使用及详解

@FunctionalInterface

注解

秒懂,Java 注解 (Annotation)你可以这样学

关于@Autowired的这些新姿势

深度分析Spring中的构造器注入

@Resource注解使用说明

Spring

Spring源码分析【综合】

【视频】手写模拟IOC

环境

如何修改Maven本地仓库位置

视频Maven教程

Maven中${}的来龙去脉

SpringBoot引用属性变量方式之 @@和${}的用法

Docker安装并使用Mysql(可用详细)

Docker下MySQL的安装 - 知乎

MySQL身份验证插件(mysql_native_password、sha256_password、caching_sha2_password)

SpringBoot实现热部署(基于2021新版idea)

Maven标准目录结构(手写Maven项目)

springboot 如何添加webapp文件夹

springboot官方推荐使用thymeleaf模板引擎,把静态资源放到resources下面的static中,然后页面放到templement中,但这次因为时间比较紧,所以我想把以前项目里面的webapp直接搬过来,里面的jsp直接使用,结果jsp能访问到,但里面的图片,css,js全都报404错误。

视频 webapp创建方式

Servlet 3.0整合Spring MVC(不使用web.xml部署描述符,使用ServletContainerInitializer)

视频 不使用web.xml整合SpringMvc

知识点

ClassPath

什么是classpath - 简书

classpath

classpath

应用通用属性【官网】

SpringBoot读取resources下的文件以及resources的资源路径

日志

Spring中的日志详细解释----记录

logback配置文件XML详解

logback-spring.xml配置详解

Redis

redisTemplate.opsForValue()中方法讲解

关于redisTemplate.opsForValue数据redis库中查不到

Spring 5 WebFlux 中的 Router Function 使用

Raw use of parameterized class ‘xxxx‘ 警告

org.springframework.boot.autoconfigure.AutoConfiguration.imports

Spring Boot引入自动配置类

Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...

SpringBoot自动配置(源码解读)

JAR介绍

Lombok工具 : 常用注解介绍 (全)

Lombok插件的简介

spring batch Job详解

SpringBoot之spring-boot-configuration-processor

spring-boot-configuration-processor的作用

【视频】spring-boot-configuration-processor

注解

史上最全spring注解,没有之一

@Documented注解的作用

@Controller,@Service,@Repository,@Component你搞懂了吗?——知乎

@Controller、@Service和@Repository注解详解

一文带你理解@RefreshScope注解实现动态刷新原理

详解@ConfigurationProperties实现原理与实战_脚本之家

@ConfigurationProperties的使用

@ConfigurationProperties注解原理与实战

【视频】注解讲解

@Value的诸多用法

【视频】@Value

@Component 和 @Bean 的区别

@Configuration源码分析

@AutoConfiguration注解详解

Spring Bean

Spring中的Bean是线程安全的吗

Spring中的Bean是线程安全的吗? - 简书

Spring中Bean的单例、多例

AOP

【视频】AOP

什么是 AOP,AOP 的作用是什么?

AOP 的详细说明以及基本的使用

一文读懂 AOP | 你想要的最全面 AOP 方法探讨

横切关注点,通知(增强),切面,目标,代理,连接点,切入点
动态代理:jdk(接口代理),cglib(继承模式)
AspectJ

Spring AOP全面详解(超级详细)

SpringAOP学习--SpringAOP简介及原理

C++之AOP

C++编译期反射实践——以AOP实现为例

事务

有关Spring事务,看这一篇就足够了

Spring事务管理详解

【视频】事务讲解

校验

Spring 参数校验详解

Spring 参数校验最佳实践及原理解析

【视频】Spring 参数校验

WebFlux

SpringWebmvc和SpringWebflux

一文弄懂 Spring WebFlux 的来龙去脉

【视频】WebFlux

Spring MVC

RequestMapping

使用

@RequestMapping 用法详解

@RequestMapping注解【params、headers、ant、占位符】

注解式控制器简介【全面】

requestMapping的params属性详解以及header属性详解

SpringMVC知识盘点及总结5@RequestMapping注解的params属性

RequestMapping支持Ant 路径风格

08第二章:05_@RequestMapping 支持Ant 路径风格

Restful风格详解

@RequestBody和@ResponseBody原理解析

@RequestBody, @ResponseBody 注解详解

@RequestBody,RequestEntity,@ResponseBody,ResponseEntity的作用与区别

SpringMVC 的请求响应扩展 ( @ResponseBody 、ResponseEntity对象 、@RequestBody 、RequestEntity对象 )

参数获取

辅助知识

注解式控制器

@RequestParam的加与不加的作用

@PathVariable、@RequestHeader与@CookieValue注解的使用案例

@ModelAttribute运用详解

【视频】参数获取解析

Spring 之 Converter转换器

【视频】自定义Converter

argumentResolvers负责解决参数绑定问题,而里面的参数转换则需要使用下图中的converters进行转换,其根据源参数类型和目标参数类型,来选择对应的转换器。

数据共享

SpringMVC学习(4)—— 域对象(作用域)

Model、ModelMap、Map有什么关系?深入底层剖析

视图解析

视图转发与重定向

第五章 视图解析与请求转发、重定向——简书

SpringMvc视图解析器详解

SpringMVC同时配置Thymeleaf和Jsp两种视图解析器

SpringMVC的视图控制器view-controller

文件上传与下载

Spring MVC 实现文件的上传和下载

基于springboot文件上传和下载

SpringBoot实现文件上传和下载 ajax

使用axios进行文件上传

8.8 Spring Boot静态资源处理

Spring boot 2 中 访问静态资源的方式

【视频】静态资源分析

过滤器和拦截器

过滤器 和 拦截器的 6个区别

过滤器和拦截器的区别

一文解决在SpringBoot项目中配置拦截器

SpringBoot项目中过滤器Filter的配置

错误处理

springboot中错误配置文件为ErrorMvcAutoConfiguration

ErrorMvcAutoConfiguration自动配置的三个组件的分析

SpringBoot-27 异常自动配置ErrorMvcAutoConfiguration

【视频】错误处理分析

DispatcherServlet::processHandlerException中处理异常:

  • @ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的

  • @ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);tomcat发送的/error

  • Spring底层的异常,如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。

异常处理流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束(requstCompletion?此处记不清);并且用 dispatchException

2、进入视图解析流程(页面渲染?)

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;

3.1、遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】

3.2、系统默认的 异常解析器;

3.2.1、DefaultErrorAttributes先来处理异常。把异常信息保存到request域,并且返回null;

3.2.2、默认没有任何人能处理异常,所以异常会被抛出

1、如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理

2、解析错误视图;遍历所有的 ErrorViewResolver 看谁能解析。

3、默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html

4、模板引擎最终响应这个页面 error/500.html (如果没有具体的错误页,则显示白页)

@ControllerAdvice

@ControllerAdvice 的介绍及三种用法

Spring @ControllerAdvice 使用及源码分析

Springmvc之HandlerExceptionResolver

SpringBoot常用注解@RestControllerAdvice

SpringBoot

SpringBoot帮助文档

从上可以看出,有多网页、单网页、PDF等几种帮助文档

【视频】SpringBoot讲解

SpringBoot2零基础入门教程

SpringBoot启动流程解析

[SpringBoot源码剖析六] 自动配置SpringMVC - 知乎

监控

SpringBoot四大核心之actuator

springboot admin-server的使用

【视频】监控

SpringCloud

问题

java: 错误: 无效的源发行版:17

field injection is not recommended

Spring请求分析

Spring Boot配置Tomcat容器、Jetty容器、Undertow容器

Handler(Undertow容器)

ServletInitialHandler

PredicateHandler

SendErrorPageHandler

PredicateHandler

SecurityInitialHandler

CachedAuthenticatedSessionHandler

AuthenticationMechanismsHandler

ServletConfidentialityConstraintHandler

PredicateHandler

ServletAuthenticationCallHandler

SSLInformationAssociationHandler

RedirectDirHandler

ServletDispatchingHandler

ServletSecurityRoleHandler

FilterHandler

  1. public class FilterHandler implements HttpHandler {
  2. // ...
  3. public void handleRequest(HttpServerExchange exchange) throws Exception {
  4. ServletRequestContext servletRequestContext = (ServletRequestContext)exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
  5. ServletRequest request = servletRequestContext.getServletRequest();
  6. ServletResponse response = servletRequestContext.getServletResponse();
  7. DispatcherType dispatcher = servletRequestContext.getDispatcherType();
  8. Boolean supported = (Boolean)this.asyncSupported.get(dispatcher);
  9. if (supported != null && !supported) {
  10. servletRequestContext.setAsyncSupported(false);
  11. }
  12. List<ManagedFilter> filters = (List)this.filters.get(dispatcher);
  13. if (filters == null) {
  14. this.next.handleRequest(exchange);
  15. } else {
  16. FilterChainImpl filterChain = new FilterChainImpl(exchange, filters, this.next, this.allowNonStandardWrappers);
  17. filterChain.doFilter(request, response);
  18. }
  19. }
  20. // ...
  21. }

FilterHandler中如果含有ManagedFilter,则通过过滤链执行ManagedFilter

此处this.filters的值为:

this.filters.get(dispatcher)dispatcherREQUEST,过滤后filters的值为:

FilterChainImpl执行ManagedFilter
  1. private static class FilterChainImpl implements FilterChain {
  2. //...
  3. @Override
  4. public void doFilter(final ServletRequest request, final ServletResponse response) throws IOException, ServletException {
  5. final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
  6. final ServletRequest oldReq = servletRequestContext.getServletRequest();
  7. final ServletResponse oldResp = servletRequestContext.getServletResponse();
  8. try {
  9. if(!allowNonStandardWrappers) {
  10. if(oldReq != request) {
  11. if(!(request instanceof ServletRequestWrapper)) {
  12. throw UndertowServletMessages.MESSAGES.requestWasNotOriginalOrWrapper(request);
  13. }
  14. }
  15. if(oldResp != response) {
  16. if(!(response instanceof ServletResponseWrapper)) {
  17. throw UndertowServletMessages.MESSAGES.responseWasNotOriginalOrWrapper(response);
  18. }
  19. }
  20. }
  21. servletRequestContext.setServletRequest(request);
  22. servletRequestContext.setServletResponse(response);
  23. int index = location++;
  24. if (index >= filters.size()) {
  25. next.handleRequest(exchange);
  26. } else {
  27. filters.get(index).doFilter(request, response, this);
  28. }
  29. } catch (IOException e) {
  30. throw e;
  31. } catch (ServletException e) {
  32. throw e;
  33. } catch (RuntimeException e) {
  34. throw e;
  35. } catch (Exception e) {
  36. throw new RuntimeException(e);
  37. } finally {
  38. location--;
  39. servletRequestContext.setServletRequest(oldReq);
  40. servletRequestContext.setServletResponse(oldResp);
  41. }
  42. }
  43. //...
  44. }

FilterChainImpl中依次执行ManagedFilter里的Filter对象,依次为:OrderedCharacterEncodingFilter,WebMvcMetricsFilter,OrderedFormContentFilter,OrderedRequestContextFilter,DelegatingFilterProxyRegistrationBean$1

OrderedCharacterEncodingFilter

编码转换过滤器?

OrderedRequestContextFilter

ServletRequestAttributes?

DelegatingFilterProxyRegistrationBean
  1. public class DelegatingFilterProxy extends GenericFilterBean {
  2. private volatile Filter delegate;
  3. //...
  4. protected void invokeDelegate(
  5. Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
  6. throws ServletException, IOException {
  7. delegate.doFilter(request, response, filterChain);
  8. }
  9. //...
  10. }

DelegatingFilterProxy中含有delegate对象,其值为FilterChainProxy,其会执行代理对象中的过滤链,详细分析见FilterChainProxy段。

ServletHandler

负责调用servlet的处理程序

  1. public class ServletHandler implements HttpHandler {
  2.     //...
  3.     @Override
  4. public void handleRequest(final HttpServerExchange exchange) throws IOException, ServletException {
  5.     //...
  6.         servlet.getInstance().service(request, response);
  7.     //...   
  8.     }
  9. //...
  10. }

依次会调用HttpServlet.service,FrameworkServlet.service,HttpServlet.service处理各种控制请求。控制请求分析,请参考SpringMvc章节。

HttpServlet
  1. public abstract class HttpServlet extends GenericServlet {
  2. //...
  3.     @Override
  4. public void service(ServletRequest req, ServletResponse res)
  5. throws ServletException, IOException
  6. {
  7. HttpServletRequest request;
  8. HttpServletResponse response;
  9. if (!(req instanceof HttpServletRequest &&
  10. res instanceof HttpServletResponse)) {
  11. throw new ServletException("non-HTTP request or response");
  12. }
  13. request = (HttpServletRequest) req;
  14. response = (HttpServletResponse) res;
  15. service(request, response);
  16. }
  17.     protected void service(HttpServletRequest req, HttpServletResponse resp)
  18. throws ServletException, IOException
  19. {
  20. String method = req.getMethod();
  21. if (method.equals(METHOD_GET)) {
  22. long lastModified = getLastModified(req);
  23. if (lastModified == -1) {
  24. // servlet doesn't support if-modified-since, no reason
  25. // to go through further expensive logic
  26. doGet(req, resp);
  27. } else {
  28. long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
  29. if (ifModifiedSince < lastModified) {
  30. // If the servlet mod time is later, call doGet()
  31. // Round down to the nearest second for a proper compare
  32. // A ifModifiedSince of -1 will always be less
  33. maybeSetLastModified(resp, lastModified);
  34. doGet(req, resp);
  35. } else {
  36. resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
  37. }
  38. }
  39. } else if (method.equals(METHOD_HEAD)) {
  40. long lastModified = getLastModified(req);
  41. maybeSetLastModified(resp, lastModified);
  42. doHead(req, resp);
  43. } else if (method.equals(METHOD_POST)) {
  44. doPost(req, resp);
  45. } else if (method.equals(METHOD_PUT)) {
  46. doPut(req, resp);
  47. } else if (method.equals(METHOD_DELETE)) {
  48. doDelete(req, resp);
  49. } else if (method.equals(METHOD_OPTIONS)) {
  50. doOptions(req,resp);
  51. } else if (method.equals(METHOD_TRACE)) {
  52. doTrace(req,resp);
  53. } else {
  54. //
  55. // Note that this means NO servlet supports whatever
  56. // method was requested, anywhere on this server.
  57. //
  58. String errMsg = lStrings.getString("http.method_not_implemented");
  59. Object[] errArgs = new Object[1];
  60. errArgs[0] = method;
  61. errMsg = MessageFormat.format(errMsg, errArgs);
  62. resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
  63. }
  64. }
  65. //...
  66. }
FrameworkServlet
  1. public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
  2. //...
  3.     @Override
  4. protected final void doGet(HttpServletRequest request, HttpServletResponse response)
  5. throws ServletException, IOException {
  6. processRequest(request, response);
  7. }
  8. //...
  9. }

SpringSecurity认证流程(Pig Auth)

pig 生成token (认证)详解 ⭐ · 语雀

FilterChainProxy

SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain:

  1. public class FilterChainProxy extends GenericFilterBean {
  2.     private static final class VirtualFilterChain implements FilterChain {
  3.         private final List<Filter> additionalFilters;
  4.     }
  5. }

其中additionalFilters的值为:

LogoutFilter

该过滤器处理路径为/logout的请求【GET、POST、PUT、DELETE】。

OAuth2AuthorizationEndpointFilter

该过滤器处理路径为/oauth2/authorize的请求。

30-OAuth2授权端点配置类

  1. public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter {
  2.     // ...
  3.     @Override
  4. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  5. throws ServletException, IOException {
  6.         Authentication authenticationRequest = this.authenticationConverter.convert(request);
  7. if (authenticationRequest instanceof AbstractAuthenticationToken) {
  8. ((AbstractAuthenticationToken) authenticationRequest).setDetails(
  9. this.authenticationDetailsSource.buildDetails(request));
  10. }
  11. if (authenticationRequest != null) {
  12. validateClientIdentifier(authenticationRequest);
  13. Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
  14. this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);
  15. }
  16. filterChain.doFilter(request, response);
  17.     }
  18. // ...
  19. }

1、this.authenticationConverter.convert中converters的值为:

OAuth2ClientAuthenticationFilter

该过滤器处理路径为【post】:/oauth2/token,/oauth2/introspect,/oauth2/revoke的请求。

客户端认证 OAuth2ClientAuthenticationFilter

  1. // OAuth2ClientAuthenticationFilter
  2. public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter {
  3. // ...
  4. // authenticationConverter 为 DelegatingAuthenticationConverter
  5. // 登录时带Authorization返回的是 ClientSecretBasicAuthenticationConverter
  6. // 登录成功后带jwt,返回的是 JwtClientAssertionAuthenticationConverter
  7. Authentication authenticationRequest = this.authenticationConverter.convert(request);
  8. Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
  9. // ...
  10. }
  11. public final class DelegatingAuthenticationConverter implements AuthenticationConverter {
  12. // ...
  13. public Authentication convert(HttpServletRequest request) {
  14. Assert.notNull(request, "request cannot be null");
  15. // converters 有四个:
  16. // JwtClientAssertionAuthenticationConverter,处理JWT
  17. // ClientSecretBasicAuthenticationConverter,处理头部Authorization
  18. // ClientSecretPostAuthenticationConverter,处理参数client_id和client_secret
  19. // PublicClientAuthenticationConverter,PKCE,grant_type,code,code_verifier
  20. for (AuthenticationConverter converter : this.converters) {
  21. Authentication authentication = converter.convert(request);
  22. if (authentication != null) {
  23. return authentication;
  24. }
  25. }
  26. return null;
  27. }
  28. }

通过AuthenticationConverter创建相应的Authentication,然后调用ProviderManager类进行认证:

  1. public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
  2. // ...
  3. @Override
  4. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  5. // getProviders() 的值为:
  6. // AnonymousAuthenticationProvider
  7. // JwtClientAssertionAuthenticationProvider@19312
  8. // ClientSecretAuthenticationProvider@19402
  9. // PublicClientAuthenticationProvider@19406
  10. // OAuth2AuthorizationCodeRequestAuthenticationProvider@19407
  11. // ...
  12. for (AuthenticationProvider provider : getProviders()) {
  13. if (!provider.supports(toTest)) {
  14. continue;
  15. }
  16. result = provider.authenticate(authentication);
  17. if (result != null) {
  18. copyDetails(authentication, result);
  19. break;
  20. }
  21. }
  22. }
  23. // ...
  24. }

1、getProviders()的值为:

未登陆时,AuthenticationConverter为ClientSecretBasicAuthenticationConverter,AuthenticationProvider为:ClientSecretAuthenticationProvider

UsernamePasswordAuthenticationFilter

处理表单提交的身份验证。必须提供两个参数:用户名和密码。

  1. public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  2. // ...   
  3.     @Override
  4. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  5. throws AuthenticationException {
  6. if (this.postOnly && !request.getMethod().equals("POST")) {
  7. throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  8. }
  9. String username = obtainUsername(request);
  10. username = (username != null) ? username.trim() : "";
  11. String password = obtainPassword(request);
  12. password = (password != null) ? password : "";
  13. UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
  14. password);
  15. // Allow subclasses to set the "details" property
  16. setDetails(request, authRequest);
  17. return this.getAuthenticationManager().authenticate(authRequest);
  18. }
  19. // ...
  20. }
AnonymousAuthenticationFilter

匿名验证过滤器,前面的验证没有通过,则采用匿名验证

  1. public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
  2.     // ...
  3.     @Override
  4. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
  5. throws IOException, ServletException {
  6. if (SecurityContextHolder.getContext().getAuthentication() == null) {
  7. Authentication authentication = createAuthentication((HttpServletRequest) req);
  8. SecurityContext context = SecurityContextHolder.createEmptyContext();
  9. context.setAuthentication(authentication);
  10. SecurityContextHolder.setContext(context);
  11. if (this.logger.isTraceEnabled()) {
  12. this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
  13. + SecurityContextHolder.getContext().getAuthentication()));
  14. }
  15. else {
  16. this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
  17. }
  18. }
  19. chain.doFilter(req, res);
  20. }
  21. // ...
  22. }

OAuth2TokenEndpointFilter

该过滤器处理路径为【post】:/oauth2/token的请求。

令牌颁发 OAuth2TokenEndpointFilter

  1. public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
  2. // ...
  3. @Override
  4. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  5. throws ServletException, IOException {
  6.         // ==> 1
  7. Authentication authorizationGrantAuthentication = this.authenticationConverter.convert(request);
  8. OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
  9. (OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication);
  10. this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, accessTokenAuthentication);
  11. }
  12. // ...
  13. }

1、this.authenticationConverter.convert中converters的值为:

其中的DelegatingAuthenticationConverter中converters的值为:

未登陆时,AuthenticationConverter为DelegatingAuthenticationConverter中的OAuth2ResourceOwnerPasswordAuthenticationConverter,AuthenticationProvider为:OAuth2ResourceOwnerPasswordAuthenticationProvider

SpringSecurity鉴权流程(Pig admin)

FilterChainProxy

SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain,VirtualFilterChainadditionalFilters的值为:

pig 校验token (鉴权)详解

BearerTokenAuthenticationFilter

  1. public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
  2.     // ...
  3.     @Override
  4. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  5. throws ServletException, IOException {
  6. String token;
  7. try {
  8.             // bearerTokenResolver值为 ==> PigBearerTokenExtractor
  9. token = this.bearerTokenResolver.resolve(request);
  10. }
  11. catch (OAuth2AuthenticationException invalid) {
  12. this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
  13. this.authenticationEntryPoint.commence(request, response, invalid);
  14. return;
  15. }
  16. if (token == null) {
  17. this.logger.trace("Did not process request since did not find bearer token");
  18. filterChain.doFilter(request, response);
  19. return;
  20. }
  21. BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
  22. authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
  23. try {
  24.             // authenticationManager类型为ProviderManager,其内有两个Provider:
  25.             // AnonymousAuthenticationProvider 和 OpaqueTokenAuthenticationProvider
  26. AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
  27. Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
  28. SecurityContext context = SecurityContextHolder.createEmptyContext();
  29. context.setAuthentication(authenticationResult);
  30. SecurityContextHolder.setContext(context);
  31. this.securityContextRepository.saveContext(context, request, response);
  32. if (this.logger.isDebugEnabled()) {
  33. this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authenticationResult));
  34. }
  35. filterChain.doFilter(request, response);
  36. }
  37. catch (AuthenticationException failed) {
  38. SecurityContextHolder.clearContext();
  39. this.logger.trace("Failed to process authentication request", failed);
  40. this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);
  41. }
  42. }
  43. // ...
  44. }
PigBearerTokenExtractor

PigBearerTokenExtractor 用来过滤,以及获取token,Authorization头,以Bearer开头。

Authorization: Bearer 279dc4ec-db03-4d21-a849-6f3acf750b12
  1. public class PigBearerTokenExtractor implements BearerTokenResolver {
  2.     // ...
  3.     @Override
  4. public String resolve(HttpServletRequest request) {
  5. boolean match = urlProperties.getUrls().stream()
  6. .anyMatch(url -> pathMatcher.match(url, request.getRequestURI()));
  7. if (match) {
  8. return null;
  9. }
  10. final String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
  11. final String parameterToken = isParameterTokenSupportedForRequest(request)
  12. ? resolveFromRequestParameters(request) : null;
  13. if (authorizationHeaderToken != null) {
  14. if (parameterToken != null) {
  15. final BearerTokenError error = BearerTokenErrors
  16. .invalidRequest("Found multiple bearer tokens in the request");
  17. throw new OAuth2AuthenticationException(error);
  18. }
  19. return authorizationHeaderToken;
  20. }
  21. if (parameterToken != null && isParameterTokenEnabledForRequest(request)) {
  22. return parameterToken;
  23. }
  24. return null;
  25. }
  26.     private String resolveFromAuthorizationHeader(HttpServletRequest request) {
  27. String authorization = request.getHeader(this.bearerTokenHeaderName);
  28. if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
  29. return null;
  30. }
  31. Matcher matcher = authorizationPattern.matcher(authorization);
  32. if (!matcher.matches()) {
  33. BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
  34. throw new OAuth2AuthenticationException(error);
  35. }
  36. return matcher.group("token");
  37. }
  38. // ...
  39. }
OpaqueTokenAuthenticationProvider

BearerTokenAuthenticationFilter中构建BearerTokenAuthenticationToken,然后调用OpaqueTokenAuthenticationProvider进行认证。

  1. public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
  2.     //...
  3. @Override
  4. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  5. if (!(authentication instanceof BearerTokenAuthenticationToken)) {
  6. return null;
  7. }
  8. BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
  9. // principal类型为PigUser
  10.         // public class PigUser extends User implements OAuth2AuthenticatedPrincipal
  11.         OAuth2AuthenticatedPrincipal principal = getOAuth2AuthenticatedPrincipal(bearer);
  12. AbstractAuthenticationToken result = convert(principal, bearer.getToken());
  13. result.setDetails(bearer.getDetails());
  14. this.logger.debug("Authenticated token");
  15. return result;
  16. }
  17.     private OAuth2AuthenticatedPrincipal getOAuth2AuthenticatedPrincipal(BearerTokenAuthenticationToken bearer) {
  18. try {
  19.             // introspector值为 ==> PigCustomOpaqueTokenIntrospector
  20. return this.introspector.introspect(bearer.getToken());
  21. }
  22. catch (BadOpaqueTokenException failed) {
  23. this.logger.debug("Failed to authenticate since token was invalid");
  24. throw new InvalidBearerTokenException(failed.getMessage(), failed);
  25. }
  26. catch (OAuth2IntrospectionException failed) {
  27. throw new AuthenticationServiceException(failed.getMessage(), failed);
  28. }
  29. }
  30. //...
  31. }

PigCustomOpaqueTokenIntrospector
  1. public class PigCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
  2.     private final OAuth2AuthorizationService authorizationService;
  3.     @Override
  4. public OAuth2AuthenticatedPrincipal introspect(String token) {
  5.         // authorizationService 值为 PigRedisOAuth2AuthorizationService
  6. OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
  7. if (Objects.isNull(oldAuthorization)) {
  8. throw new InvalidBearerTokenException(token);
  9. }
  10. // 客户端模式默认返回
  11. if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) {
  12. return new PigClientCredentialsOAuth2AuthenticatedPrincipal(oldAuthorization.getAttributes(),
  13. AuthorityUtils.NO_AUTHORITIES, oldAuthorization.getPrincipalName());
  14. }
  15. Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
  16. .getBeansOfType(PigUserDetailsService.class);
  17. Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
  18. .filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(),
  19. oldAuthorization.getAuthorizationGrantType().getValue()))
  20. .max(Comparator.comparingInt(Ordered::getOrder));
  21. UserDetails userDetails = null;
  22. try {
  23. Object principal = Objects.requireNonNull(oldAuthorization).getAttributes().get(Principal.class.getName());
  24. UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
  25. Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();
  26. userDetails = optional.get().loadUserByUser((PigUser) tokenPrincipal);
  27. }
  28. catch (UsernameNotFoundException notFoundException) {
  29. log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage());
  30. throw notFoundException;
  31. }
  32. catch (Exception ex) {
  33. log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());
  34. }
  35. return (PigUser) userDetails;
  36. }
  37. }

introspect函数从PigRedisOAuth2AuthorizationService通过token获取OAuth2Authorization,然后再根据用户信息,去数据库中loadUserByUser获取用户信息(缓存中有则从缓存中获取)。

PigRedisOAuth2AuthorizationService
  1. public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
  2. //...
  3.     public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
  4. Assert.hasText(token, "token cannot be empty");
  5. Assert.notNull(tokenType, "tokenType cannot be empty");
  6. redisTemplate.setValueSerializer(RedisSerializer.java());
  7.         // 从 Redis中获取认证信息
  8. return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
  9. }   
  10. //...
  11. }

登陆后,后调用BearerTokenAuthenticationFilter过滤器进行认证。

接口进行权限判断

  1. @SysLog("添加部门")
  2. @PostMapping
  3. @PreAuthorize("@pms.hasPermission('sys_dept_add')")
  4. public R<Boolean> save(@Valid @RequestBody SysDept sysDept) {
  5. return R.ok(sysDeptService.saveDept(sysDept));
  6. }

关于@PreAuthorize注解的使用场景

SpringBoot - @PreAuthorize注解详解

使用AOP进行切面代理

调用逻辑

  1. DispatcherServlet::doService(HttpServletRequest request, HttpServletResponse response)
  2. // ==>
  3. DispatcherServlet::doDispatch(HttpServletRequest request, HttpServletResponse response)
  4. void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  5.     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  6. }
  7. // ==>
  8. AbstractHandlerMethodAdapter::handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  9. // ==>
  10. RequestMappingHandlerAdapter::handleInternal(HttpServletRequest request,
  11. HttpServletResponse response, HandlerMethod handlerMethod)
  12. // ==>
  13. RequestMappingHandlerAdapter::invokeHandlerMethod(HttpServletRequest request,
  14. HttpServletResponse response, HandlerMethod handlerMethod)
  15. // ==>
  16. ServletInvocableHandlerMethod::invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  17. Object... providedArgs)
  18.     public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  19. Object... providedArgs) throws Exception {
  20.         // 调用函数方法
  21. Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  22. // 如果有返回值,则处理返回值
  23. this.returnValueHandlers.handleReturnValue(
  24. returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  25. }
  26. // ==>
  27. InvocableHandlerMethod::invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  28. Object... providedArgs)
  29. public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  30. Object... providedArgs) throws Exception {
  31.         // 获取函数参数
  32. Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  33. return doInvoke(args);
  34. }
  35. // ==>
  36. InvocableHandlerMethod::doInvoke(Object... args)
  37. // ==>
  38. Method::invoke(Object obj, Object... args)
  39. // ==>
  40. DelegatingMethodAccessorImpl::invoke(Object obj, Object[] args)
  41. // ==>
  42. NativeMethodAccessorImpl::invoke(Object obj, Object[] args)
  43. // ==>
  44. CglibAopProxy::intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
  45. // ==>
  46. CglibAopProxy::proceed()
  47. // ==>
  48. ReflectiveMethodInvocation::proceed()
  49. // ==>
  50. ExposeInvocationInterceptor::invoke(MethodInvocation mi)
  51. // ==>
  52. CglibAopProxy::proceed()
  53. // ==>
  54. ReflectiveMethodInvocation::proceed()
  55. // ==>
  56. MethodSecurityInterceptor::invoke(MethodInvocation mi)
  57. // ==>
  58. AbstractSecurityInterceptor::beforeInvocation(Object object)
  59. // ==>
  60. AbstractSecurityInterceptor::attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
  61. Authentication authenticated)
  62.     private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
  63. Authentication authenticated) {
  64.         // accessDecisionManager类型为AffirmativeBased
  65.         this.accessDecisionManager.decide(authenticated, object, attributes);
  66. }
  67. // ==>
  68. AffirmativeBased::decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
  69.     public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
  70. throws AccessDeniedException {
  71. int deny = 0;
  72.         // getDecisionVoters()即decisionVoters,其值为:
  73.         // PreInvocationAuthorizationAdviceVoter
  74. // RoleVoter
  75. // AuthenticatedVoter
  76. for (AccessDecisionVoter voter : getDecisionVoters()) {
  77. int result = voter.vote(authentication, object, configAttributes);
  78. switch (result) {
  79. case AccessDecisionVoter.ACCESS_GRANTED:
  80. return;
  81. case AccessDecisionVoter.ACCESS_DENIED:
  82. deny++;
  83. break;
  84. default:
  85. break;
  86. }
  87. }
  88. if (deny > 0) {
  89. throw new AccessDeniedException(
  90. this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
  91. }
  92. // To get this far, every AccessDecisionVoter abstained
  93. checkAllowIfAllAbstainDecisions();
  94. }

AffirmativeBased::decide调用各个投票器,进行投票。没有通过则抛出AccessDeniedException异常。

PreInvocationAuthorizationAdviceVoter
  1. // ==>
  2. PreInvocationAuthorizationAdviceVoter::vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes)
  3. public int vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes) {
  4. // Find prefilter and preauth (or combined) attributes
  5. // if both null, abstain else call advice with them
  6. PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
  7. if (preAttr == null) {
  8. // No expression based metadata, so abstain
  9. return ACCESS_ABSTAIN;
  10. }
  11. // ACCESS_GRANTED 赞成,ACCESS_ABSTAIN 弃权,ACCESS_DENIED 拒绝
  12. return this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED;
  13. }
  14. // ==>
  15. ExpressionBasedPreInvocationAdvice::before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr)
  16. // ==>
  17. ExpressionUtils::evaluateAsBoolean(Expression expr, EvaluationContext ctx)
  18. // ==>
  19. SpelExpression::getValue(EvaluationContext context, @Nullable Class<T> expectedResultType)
  20. // ==>
  21. SpelNodeImpl::getTypedValue(ExpressionState expressionState)
  22. // ==>
  23. CompoundExpression::getValueInternal(ExpressionState state)
  24. // ==>
  25. MethodReference::getValue()
  26. // ==>
  27. MethodReference::getValueInternal(EvaluationContext evaluationContext,
  28. @Nullable Object value, @Nullable TypeDescriptor targetType, Object[] arguments)
  29. // ==>
  30. ReflectiveMethodExecutor::execute(EvaluationContext context, Object target, Object... arguments)
  31. // ==>
  32. Method::invoke(Object obj, Object... args)
  33. // ==>
  34. NativeMethodAccessorImpl::invoke(Object obj, Object[] args)
  35. // ==>
  36. PermissionService::hasPermission(String... permissions)
  37. // permissions的值为 sys_dept_add

最终调用到了PermissionService::hasPermission

  1. public class PermissionService {
  2. /**
  3. * 判断接口是否有任意xxx,xxx权限
  4. * @param permissions 权限
  5. * @return {boolean}
  6. */
  7. public boolean hasPermission(String... permissions) {
  8. if (ArrayUtil.isEmpty(permissions)) {
  9. return false;
  10. }
  11. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  12. if (authentication == null) {
  13. return false;
  14. }
  15. Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
  16. return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText)
  17. .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
  18. }
  19. }

Spring请求分析(Tomcat)

ValveBase

StandardEngineValve

ErrorReportValve

StandardHostValve

AuthenticatorBase

StandardContextValve

StandardWrapperValve

ApplicationFilterChain

  1. public final class ApplicationFilterChain implements FilterChain {
  2. // ...
  3.     private ApplicationFilterConfig[] filters
  4.     private void internalDoFilter(ServletRequest request,
  5. ServletResponse response)
  6. throws IOException, ServletException {
  7. // Call the next filter if there is one
  8. if (pos < n) {
  9. ApplicationFilterConfig filterConfig = filters[pos++];
  10. try {
  11. Filter filter = filterConfig.getFilter();
  12. filter.doFilter(request, response, this);
  13. }
  14. return;
  15. }
  16.     // ...
  17. // We fell off the end of the chain -- call the servlet instance
  18. try {
  19. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
  20. lastServicedRequest.set(request);
  21. lastServicedResponse.set(response);
  22. }
  23. if (request.isAsyncSupported() && !servletSupportsAsync) {
  24. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
  25. Boolean.FALSE);
  26. }
  27. // Use potentially wrapped request from this point
  28. if ((request instanceof HttpServletRequest) &&
  29. (response instanceof HttpServletResponse) &&
  30. Globals.IS_SECURITY_ENABLED ) {
  31. final ServletRequest req = request;
  32. final ServletResponse res = response;
  33. Principal principal =
  34. ((HttpServletRequest) req).getUserPrincipal();
  35. Object[] args = new Object[]{req, res};
  36. SecurityUtil.doAsPrivilege("service",
  37. servlet,
  38. classTypeUsedInService,
  39. args,
  40. principal);
  41. } else {
  42. servlet.service(request, response);
  43. }
  44. } catch (IOException | ServletException | RuntimeException e) {
  45. throw e;
  46. } catch (Throwable e) {
  47. e = ExceptionUtils.unwrapInvocationTargetException(e);
  48. ExceptionUtils.handleThrowable(e);
  49. throw new ServletException(sm.getString("filterChain.servlet"), e);
  50. } finally {
  51. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
  52. lastServicedRequest.set(null);
  53. lastServicedResponse.set(null);
  54. }
  55. }
  56. }
  57. // ...
  58. }

ApplicationFilterChainfilters对象,其是ApplicationFilterConfig数组,ApplicationFilterChain会依次执行ApplicationFilterChain里的Filter对象,依次为:OrderedCharacterEncodingFilter,OrderedFormContentFilter,OrderedRequestContextFilter,DelegatingFilterProxyRegistrationBean$1。过滤器链执行完毕后将调用servlet.service(request, response) 对servlet请求进行处理,参见SpringMvc章节。

OrderedCharacterEncodingFilter

编码转换过滤器?

OrderedRequestContextFilter

ServletRequestAttributes?

DelegatingFilterProxyRegistrationBean
  1. public class DelegatingFilterProxy extends GenericFilterBean {
  2. private volatile Filter delegate;
  3. //...
  4. protected void invokeDelegate(
  5. Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
  6. throws ServletException, IOException {
  7. delegate.doFilter(request, response, filterChain);
  8. }
  9. //...
  10. }

DelegatingFilterProxy中含有delegate对象,其值为FilterChainProxy,其会执行代理对象中的过滤链,详细分析见FilterChainProxy段。

SpringSecurity认证流程(youlai Auth)

FilterChainProxy

SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain,VirtualFilterChainadditionalFilters的值为:

BasicAuthenticationFilter
  1. public class BasicAuthenticationFilter extends OncePerRequestFilter {
  2.     //...
  3.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
  4. throws IOException, ServletException {
  5. try {
  6.             // authenticationConverter 类型为 BasicAuthenticationConverter
  7. UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
  8. if (authRequest == null) {
  9. this.logger.trace("Did not process authentication request since failed to find "
  10. + "username and password in Basic Authorization header");
  11. chain.doFilter(request, response);
  12. return;
  13. }
  14. String username = authRequest.getName();
  15. this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
  16. if (authenticationIsRequired(username)) {
  17.                 // 开始认证,authenticationManager中含有两个Provider
  18.                 // AnonymousAuthenticationProvider
  19. // DaoAuthenticationProvider
  20. Authentication authResult = this.authenticationManager.authenticate(authRequest);
  21. SecurityContext context = SecurityContextHolder.createEmptyContext();
  22. context.setAuthentication(authResult);
  23. SecurityContextHolder.setContext(context);
  24. if (this.logger.isDebugEnabled()) {
  25. this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
  26. }
  27. this.rememberMeServices.loginSuccess(request, response, authResult);
  28. this.securityContextRepository.saveContext(context, request, response);
  29. onSuccessfulAuthentication(request, response, authResult);
  30. }
  31. }
  32. catch (AuthenticationException ex) {
  33. SecurityContextHolder.clearContext();
  34. this.logger.debug("Failed to process authentication request", ex);
  35. this.rememberMeServices.loginFail(request, response);
  36. onUnsuccessfulAuthentication(request, response, ex);
  37. if (this.ignoreFailure) {
  38. chain.doFilter(request, response);
  39. }
  40. else {
  41. this.authenticationEntryPoint.commence(request, response, ex);
  42. }
  43. return;
  44. }
  45. chain.doFilter(request, response);
  46. }
  47. //...
  48. }
BasicAuthenticationConverter

BasicAuthenticationConverter调用convert(HttpServletRequest request)获得UsernamePasswordAuthenticationToken。解析头字段为Authorization的值,其为Base64加密,解析后值为mall-admin:123456。

Authorization: Basic bWFsbC1hZG1pbjoxMjM0NTY=
  1. public class BasicAuthenticationConverter implements AuthenticationConverter {
  2. //...
  3.     public UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {
  4. String header = request.getHeader(HttpHeaders.AUTHORIZATION);
  5. if (header == null) {
  6. return null;
  7. }
  8. header = header.trim();
  9. if (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {
  10. return null;
  11. }
  12. if (header.equalsIgnoreCase(AUTHENTICATION_SCHEME_BASIC)) {
  13. throw new BadCredentialsException("Empty basic authentication token");
  14. }
  15. byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
  16. byte[] decoded = decode(base64Token);
  17. String token = new String(decoded, getCredentialsCharset(request));
  18. int delim = token.indexOf(":");
  19. if (delim == -1) {
  20. throw new BadCredentialsException("Invalid basic authentication token");
  21. }
  22. UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken
  23. .unauthenticated(token.substring(0, delim), token.substring(delim + 1));
  24. result.setDetails(this.authenticationDetailsSource.buildDetails(request));
  25. return result;
  26. }
  27. //...   
  28. }
DaoAuthenticationProvider
  1. public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
  2.     //...
  3.     // authenticate此段代码在AbstractUserDetailsAuthenticationProvider类中
  4.     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  5.         // 从缓存中找,没有启用缓存则为空
  6.         UserDetails user = this.userCache.getUserFromCache(username);
  7. if (user == null) {
  8.             // 最后调用 JdbcClientDetailsService中loadClientByClientId方法
  9. user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
  10.         }
  11.     }
  12. //...
  13. }
JdbcClientDetailsService
  1. public class JdbcClientDetailsService implements ClientDetailsService, ClientRegistrationService {
  2.     public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
  3. ClientDetails details;
  4. try {
  5. details = jdbcTemplate.queryForObject(selectClientDetailsSql, new ClientDetailsRowMapper(), clientId);
  6. }
  7. catch (EmptyResultDataAccessException e) {
  8. throw new NoSuchClientException("No client with requested id: " + clientId);
  9. }
  10. return details;
  11. }
  12. }

details值为:

最终DaoAuthenticationProvider进行了认证,即BasicAuthenticationFilter响应了认证。

SpringSecurity鉴权流程(youlai admin)

FilterChainProxy

SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain,VirtualFilterChainadditionalFilters的值为:

BearerTokenAuthenticationFilter
  1. public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
  2. // ...
  3. @Override
  4. protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  5. throws ServletException, IOException {
  6. String token;
  7. try {
  8. // bearerTokenResolver值为 ==> DefaultBearerTokenResolver
  9. token = this.bearerTokenResolver.resolve(request);
  10. }
  11. catch (OAuth2AuthenticationException invalid) {
  12. this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
  13. this.authenticationEntryPoint.commence(request, response, invalid);
  14. return;
  15. }
  16. if (token == null) {
  17. this.logger.trace("Did not process request since did not find bearer token");
  18. filterChain.doFilter(request, response);
  19. return;
  20. }
  21. BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
  22. authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
  23. try {
  24. // authenticationManager类型为ProviderManager,其内有两个Provider:
  25. // AnonymousAuthenticationProvider 和 JwtAuthenticationProvider
  26. AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
  27. Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
  28. SecurityContext context = SecurityContextHolder.createEmptyContext();
  29. context.setAuthentication(authenticationResult);
  30. SecurityContextHolder.setContext(context);
  31. this.securityContextRepository.saveContext(context, request, response);
  32. if (this.logger.isDebugEnabled()) {
  33. this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authenticationResult));
  34. }
  35. filterChain.doFilter(request, response);
  36. }
  37. catch (AuthenticationException failed) {
  38. SecurityContextHolder.clearContext();
  39. this.logger.trace("Failed to process authentication request", failed);
  40. this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);
  41. }
  42. }
  43. // ...
  44. }
DefaultBearerTokenResolver

DefaultBearerTokenResolver 用来过滤,以及获取token,Authorization头,以Basic开头,由于没有以bearer开头,此处获取的token为空。

Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJkZXB0SWQiOjIsImV4cCI6MTY3ODk2MjQ0NywiZGF0YVNjb3BlIjowLCJ1c2VySWQiOjIsImF1dGhvcml0aWVzIjpbIkFETUlOIl0sImp0aSI6Im82ZUVLQzhoNGJoSDVIUHZVZ0huSTR2NTFDVSIsImNsaWVudF9pZCI6Im1hbGwtYWRtaW4iLCJ1c2VybmFtZSI6ImFkbWluIn0.X4rJ50cX32EebSq0SftyQMaRf0bd1VZTvlO4OEeSm8RX-PNQ_OQ3Xq03jTMi9JkAiXTLe8BCaVm1Z0JLOVfoXyyJVmK9UsLFr5uC0wtFG7Q_Cei7hdCjU_6sJekFti0MJg4W8nU_DpYtalNUwgBrbuhu4WEw2dno6Tg0oha-Z6gKYj3U4LIVR3cIMjLUY4IB2aTzewNgRWwBs_SN6e0Z-59mSsdKrksr3-d7lpg-A19-D745IEdFK3o8ERjDkJqltk9yqfX8_Vw6tn_r8V5Kdmit7oTc0qG9Qcl15ubJF4U08IHH_XVvqAe2WNtLHF2I7Rhls-pYCgEVhdsto5oEjQ
  1. public final class DefaultBearerTokenResolver implements BearerTokenResolver {
  2. // ...
  3. @Override
  4. public String resolve(final HttpServletRequest request) {
  5. final String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
  6. final String parameterToken = isParameterTokenSupportedForRequest(request)
  7. ? resolveFromRequestParameters(request) : null;
  8. if (authorizationHeaderToken != null) {
  9. if (parameterToken != null) {
  10. final BearerTokenError error = BearerTokenErrors
  11. .invalidRequest("Found multiple bearer tokens in the request");
  12. throw new OAuth2AuthenticationException(error);
  13. }
  14. return authorizationHeaderToken;
  15. }
  16. if (parameterToken != null && isParameterTokenEnabledForRequest(request)) {
  17. return parameterToken;
  18. }
  19. return null;
  20. }
  21. private String resolveFromAuthorizationHeader(HttpServletRequest request) {
  22. String authorization = request.getHeader(this.bearerTokenHeaderName);
  23. if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
  24. return null;
  25. }
  26. Matcher matcher = authorizationPattern.matcher(authorization);
  27. if (!matcher.matches()) {
  28. BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
  29. throw new OAuth2AuthenticationException(error);
  30. }
  31. return matcher.group("token");
  32. }
  33. // ...
  34. }
JwtAuthenticationProvider
  1. public final class JwtAuthenticationProvider implements AuthenticationProvider {
  2. // ...
  3.     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  4. BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
  5. Jwt jwt = getJwt(bearer);
  6.         // jwtAuthenticationConverter 类型为 JwtAuthenticationConverter
  7. AbstractAuthenticationToken token = this.jwtAuthenticationConverter.convert(jwt);
  8. token.setDetails(bearer.getDetails());
  9. this.logger.debug("Authenticated token");
  10. return token;
  11. }
  12. private Jwt getJwt(BearerTokenAuthenticationToken bearer) {
  13. try {
  14. return this.jwtDecoder.decode(bearer.getToken());
  15. }
  16. catch (BadJwtException failed) {
  17. this.logger.debug("Failed to authenticate since the JWT was invalid");
  18. throw new InvalidBearerTokenException(failed.getMessage(), failed);
  19. }
  20. catch (JwtException failed) {
  21. throw new AuthenticationServiceException(failed.getMessage(), failed);
  22. }
  23. }
  24. // ...
  25. }

getJwt函数中解密了token字段,Jwt jwt的值为:

JwtAuthenticationConverter
  1. public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
  2. // ...
  3.     public final AbstractAuthenticationToken convert(Jwt jwt) {
  4. Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
  5. String principalClaimValue = jwt.getClaimAsString(this.principalClaimName);
  6. return new JwtAuthenticationToken(jwt, authorities, principalClaimValue);
  7. }
  8. // ...   
  9. }

实现了JWT认证。

AnonymousAuthenticationFilter
  1. public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
  2. // ...
  3.     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
  4. throws IOException, ServletException {
  5. if (SecurityContextHolder.getContext().getAuthentication() == null) {
  6. Authentication authentication = createAuthentication((HttpServletRequest) req);
  7. SecurityContext context = SecurityContextHolder.createEmptyContext();
  8. context.setAuthentication(authentication);
  9. SecurityContextHolder.setContext(context);
  10. if (this.logger.isTraceEnabled()) {
  11. this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
  12. + SecurityContextHolder.getContext().getAuthentication()));
  13. }
  14. else {
  15. this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
  16. }
  17. }
  18. else {
  19. if (this.logger.isTraceEnabled()) {
  20. this.logger.trace(LogMessage.of(() -> "Did not set SecurityContextHolder since already authenticated "
  21. + SecurityContextHolder.getContext().getAuthentication()));
  22. }
  23. }
  24. chain.doFilter(req, res);
  25. }
  26. // ...
  27. }

创建匿名认证

接口进行权限判断

  1. @ApiOperation(value = "新增用户")
  2. @PostMapping
  3. @PreAuthorize("@pms.hasPermission('sys:user:add')")
  4. public Result saveUser(
  5. @Validate @RequestBody UserForm userForm
  6. ) {
  7. boolean result = userService.saveUser(userForm);
  8. return Result.judge(result);
  9. }

PermissionService

  1. @Service("pms")
  2. @RequiredArgsConstructor
  3. public class PermissionService {
  4. private final RedisTemplate redisTemplate;
  5. public boolean hasPermission(String perm) {
  6. if (StrUtil.isBlank(perm)) {
  7. return false;
  8. }
  9. if (SecurityUtils.isRoot()) {
  10. return true;
  11. }
  12. Long userId = SecurityUtils.getUserId();
  13. Set<String> perms = (Set<String>) redisTemplate.opsForValue().get("AUTH:USER_PERMS:" + userId);
  14. if (CollectionUtil.isEmpty(perms)) {
  15. return false;
  16. }
  17. return perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
  18. }
  19. }
  20. public class SecurityUtils {
  21. public static Long getUserId() {
  22. Long userId = Convert.toLong(getTokenAttributes().get("userId"));
  23. return userId;
  24. }
  25. public static Map<String, Object> getTokenAttributes() {
  26. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  27. if (authentication != null) {
  28. if (authentication instanceof JwtAuthenticationToken) {
  29. JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
  30. Map<String, Object> tokenAttributes = jwtAuthenticationToken.getTokenAttributes();
  31. return tokenAttributes;
  32. }
  33. }
  34. return Collections.EMPTY_MAP;
  35. }   
  36. }

jwtAuthenticationToken.getTokenAttributes()

  1. public class JwtAuthenticationToken extends AbstractOAuth2TokenAuthenticationToken<Jwt> {
  2.     //...
  3.     public Map<String, Object> getTokenAttributes() {
  4. return this.getToken().getClaims();
  5. }
  6.     // token中保存的为JWT对象
  7.     public final T getToken() {
  8. return this.token;
  9. }
  10. //...
  11. }
  12. public class Jwt extends AbstractOAuth2Token implements JwtClaimAccessor {
  13. //...
  14.     private final Map<String, Object> claims;
  15.     public Map<String, Object> getClaims() {
  16. return this.claims;
  17. }
  18. //...
  19. }

claims中保存的值为:

Filter的创建(请求分析的补充)

JWT

JWT公钥设置

  1. spring:
  2. security:
  3. oauth2:
  4. resourceserver:
  5. jwt:
  6. jwk-set-uri: http://localhost:9999/youlai-auth/rsa/publicKey

此处相关的处理代码在org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties中。

  1. @ConfigurationProperties(prefix = "spring.security.oauth2.resourceserver")
  2. public class OAuth2ResourceServerProperties {
  3.     //...
  4. private final Jwt jwt = new Jwt();
  5.     public static class Jwt {
  6. /**
  7. * JSON Web Key URI to use to verify the JWT token.
  8. */
  9. private String jwkSetUri;
  10.         private Resource publicKeyLocation;
  11.     //...
  12.     }
  13. //...
  14. }

认证

pom中引入了:

  1. <!-- OAuth2 认证服务器-->
  2. <dependency>
  3. <groupId>org.springframework.security.oauth.boot</groupId>
  4. <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  5. </dependency>

org/springframework/boot/autoconfigure/security/oauth2/authserver下有:

AuthorizationServerProperties.java,AuthorizationServerTokenServicesConfiguration.java,OAuth2AuthorizationServerConfiguration.java三个文件。

鉴权(资源服务器)

pom中引入了:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
  4. </dependency>

org.springframework.boot.autoconfigure.security.oauth2.resource

resource下有:OAuth2ResourceServerConfiguration 配置类

ResourceServerConfig

com.youlai.common.security.config.ResourceServerConfig

  1. @ConfigurationProperties(prefix = "security")
  2. @Configuration
  3. @EnableWebSecurity
  4. @EnableGlobalMethodSecurity(prePostEnabled = true)
  5. @Slf4j
  6. @RequiredArgsConstructor
  7. public class ResourceServerConfig {
  8.     @Bean
  9. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  10. if (CollectionUtil.isEmpty(ignoreUrls)) {
  11. ignoreUrls = Arrays.asList("/webjars/**", "/doc.html", "/swagger-resources/**", "/v2/api-docs");
  12. }
  13. log.info("whitelist path:{}", JSONUtil.toJsonStr(ignoreUrls));
  14. http
  15. .csrf().disable()
  16. .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  17. .and()
  18. .authorizeRequests()
  19. .antMatchers(Convert.toStrArray(ignoreUrls)).permitAll()
  20. .anyRequest().authenticated()
  21. ;
  22. http.oauth2ResourceServer()
  23. .jwt()
  24. .jwtAuthenticationConverter(jwtAuthenticationConverter())
  25. .and()
  26. .authenticationEntryPoint(authenticationEntryPoint)
  27. .accessDeniedHandler(accessDeniedHandler)
  28. ;
  29. return http.build();
  30. }
  31.     //...
  32. }

http.oauth2ResourceServer().jwt()中默认创建的就是BearerTokenAuthenticationFilter。

Filter

认证

org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder

  1. public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
  2. extends AbstractSecurityBuilder<O> {
  3.     private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers;
  4. }

AbstractConfiguredSecurityBuilder中configurers决定了创建什么类型的filter。

WebSecurityConfiguration

org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration

HttpBasicConfigurer
  1. public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
  2. extends AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {
  3.     //...
  4. @Override
  5. public void configure(B http) {
  6. AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
  7. BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager,
  8. this.authenticationEntryPoint);
  9. if (this.authenticationDetailsSource != null) {
  10. basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
  11. }
  12. RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
  13. if (rememberMeServices != null) {
  14. basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
  15. }
  16. basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
  17. http.addFilter(basicAuthenticationFilter);
  18. }
  19. }

HttpBasicConfigurerconfigure函数中创建BasicAuthenticationFilter

AuthorizationServerSecurityConfiguration

org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration

PIG
OAuth2AuthorizationServerConfigurer

org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer

  1. public final class OAuth2AuthorizationServerConfigurer
  2. extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer, HttpSecurity> {
  3.     //...
  4. public void configure(HttpSecurity httpSecurity) {
  5. this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
  6. AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
  7. AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(authorizationServerSettings);
  8. httpSecurity.addFilterAfter(postProcess(authorizationServerContextFilter), SecurityContextHolderFilter.class);
  9. JWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);
  10. if (jwkSource != null) {
  11. NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(
  12. jwkSource, authorizationServerSettings.getJwkSetEndpoint());
  13. httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
  14. }
  15. }
  16.     //...
  17. }

OAuth2ClientAuthenticationFilter ?

鉴权

OAuth2ResourceServerConfigurer

org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration

  1. public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
  2. extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
  3.     //...
  4.     public void configure(H http) {
  5. BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
  6. this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
  7. AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
  8. if (resolver == null) {
  9. AuthenticationManager authenticationManager = getAuthenticationManager(http);
  10. resolver = (request) -> authenticationManager;
  11. }
  12. BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
  13. filter.setBearerTokenResolver(bearerTokenResolver);
  14. filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
  15. filter = postProcess(filter);
  16. http.addFilter(filter);
  17. }
  18.     //...
  19. }

OAuth2ResourceServerConfigurerconfigure函数中创建BearerTokenAuthenticationFilter

HTML

HTML视频合集

JavaScript

Css

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/716472
推荐阅读
相关标签
  

闽ICP备14008679号