赞
踩
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。
SpringBoot 基于 Spring 开发。SpringBoot 本身并不提供 Spring 框架的核心特性以及扩展功能,也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。
关于 SpringBoot 有一句很出名的话就是约定大于配置。采用 Spring Boot 可以大大的简化开发模式,它集成了大量常用的第三方库配置,所有你想集成的常用框架,它都有对应的组件支持,例如 Redis、MongoDB、Jpa、kafka,Hakira 等等。SpringBoot 应用中这些第三方库几乎可以零配置地开箱即用,大部分的 SpringBoot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
MySQL身份验证插件(mysql_native_password、sha256_password、caching_sha2_password)
springboot官方推荐使用thymeleaf模板引擎,把静态资源放到resources下面的static中,然后页面放到templement中,但这次因为时间比较紧,所以我想把以前项目里面的webapp直接搬过来,里面的jsp直接使用,结果jsp能访问到,但里面的图片,css,js全都报404错误。
Servlet 3.0整合Spring MVC(不使用web.xml部署描述符,使用ServletContainerInitializer)
SpringBoot读取resources下的文件以及resources的资源路径
redisTemplate.opsForValue()中方法讲解
关于redisTemplate.opsForValue数据redis库中查不到
Spring 5 WebFlux 中的 Router Function 使用
Raw use of parameterized class ‘xxxx‘ 警告
Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...
SpringBoot之spring-boot-configuration-processor
spring-boot-configuration-processor的作用
【视频】spring-boot-configuration-processor
@Controller,@Service,@Repository,@Component你搞懂了吗?——知乎
@Controller、@Service和@Repository注解详解
详解@ConfigurationProperties实现原理与实战_脚本之家
@ConfigurationProperties注解原理与实战
横切关注点,通知(增强),切面,目标,代理,连接点,切入点
动态代理:jdk(接口代理),cglib(继承模式)
AspectJ
@RequestMapping注解【params、headers、ant、占位符】
requestMapping的params属性详解以及header属性详解
SpringMVC知识盘点及总结5@RequestMapping注解的params属性
08第二章:05_@RequestMapping 支持Ant 路径风格
@RequestBody和@ResponseBody原理解析
@RequestBody, @ResponseBody 注解详解
@RequestBody,RequestEntity,@ResponseBody,ResponseEntity的作用与区别
SpringMVC 的请求响应扩展 ( @ResponseBody 、ResponseEntity对象 、@RequestBody 、RequestEntity对象 )
@PathVariable、@RequestHeader与@CookieValue注解的使用案例
argumentResolvers负责解决参数绑定问题,而里面的参数转换则需要使用下图中的converters进行转换,其根据源参数类型和目标参数类型,来选择对应的转换器。
Model、ModelMap、Map有什么关系?深入底层剖析
SpringMVC同时配置Thymeleaf和Jsp两种视图解析器
SpringMVC的视图控制器view-controller
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 (如果没有具体的错误页,则显示白页)
Spring @ControllerAdvice 使用及源码分析
Springmvc之HandlerExceptionResolver
SpringBoot常用注解@RestControllerAdvice
从上可以看出,有多网页、单网页、PDF等几种帮助文档
[SpringBoot源码剖析六] 自动配置SpringMVC - 知乎
field injection is not recommended
Spring Boot配置Tomcat容器、Jetty容器、Undertow容器
- public class FilterHandler implements HttpHandler {
- // ...
- public void handleRequest(HttpServerExchange exchange) throws Exception {
- ServletRequestContext servletRequestContext = (ServletRequestContext)exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
- ServletRequest request = servletRequestContext.getServletRequest();
- ServletResponse response = servletRequestContext.getServletResponse();
- DispatcherType dispatcher = servletRequestContext.getDispatcherType();
- Boolean supported = (Boolean)this.asyncSupported.get(dispatcher);
- if (supported != null && !supported) {
- servletRequestContext.setAsyncSupported(false);
- }
-
- List<ManagedFilter> filters = (List)this.filters.get(dispatcher);
- if (filters == null) {
- this.next.handleRequest(exchange);
- } else {
- FilterChainImpl filterChain = new FilterChainImpl(exchange, filters, this.next, this.allowNonStandardWrappers);
- filterChain.doFilter(request, response);
- }
- }
- // ...
- }
FilterHandler中如果含有ManagedFilter,则通过过滤链执行ManagedFilter。
此处this.filters的值为:
this.filters.get(dispatcher)中dispatcher为REQUEST,过滤后filters的值为:
- private static class FilterChainImpl implements FilterChain {
- //...
- @Override
- public void doFilter(final ServletRequest request, final ServletResponse response) throws IOException, ServletException {
- final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
- final ServletRequest oldReq = servletRequestContext.getServletRequest();
- final ServletResponse oldResp = servletRequestContext.getServletResponse();
- try {
- if(!allowNonStandardWrappers) {
- if(oldReq != request) {
- if(!(request instanceof ServletRequestWrapper)) {
- throw UndertowServletMessages.MESSAGES.requestWasNotOriginalOrWrapper(request);
- }
- }
- if(oldResp != response) {
- if(!(response instanceof ServletResponseWrapper)) {
- throw UndertowServletMessages.MESSAGES.responseWasNotOriginalOrWrapper(response);
- }
- }
- }
- servletRequestContext.setServletRequest(request);
- servletRequestContext.setServletResponse(response);
- int index = location++;
- if (index >= filters.size()) {
- next.handleRequest(exchange);
- } else {
- filters.get(index).doFilter(request, response, this);
- }
- } catch (IOException e) {
- throw e;
- } catch (ServletException e) {
- throw e;
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeException(e);
- } finally {
- location--;
- servletRequestContext.setServletRequest(oldReq);
- servletRequestContext.setServletResponse(oldResp);
- }
- }
- //...
- }
FilterChainImpl中依次执行ManagedFilter里的Filter对象,依次为:OrderedCharacterEncodingFilter,WebMvcMetricsFilter,OrderedFormContentFilter,OrderedRequestContextFilter,DelegatingFilterProxyRegistrationBean$1
编码转换过滤器?
ServletRequestAttributes?
- public class DelegatingFilterProxy extends GenericFilterBean {
- private volatile Filter delegate;
- //...
- protected void invokeDelegate(
- Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- delegate.doFilter(request, response, filterChain);
- }
- //...
- }
DelegatingFilterProxy中含有delegate对象,其值为FilterChainProxy,其会执行代理对象中的过滤链,详细分析见FilterChainProxy段。
负责调用servlet的处理程序
- public class ServletHandler implements HttpHandler {
- //...
- @Override
- public void handleRequest(final HttpServerExchange exchange) throws IOException, ServletException {
- //...
- servlet.getInstance().service(request, response);
- //...
- }
- //...
- }
依次会调用HttpServlet.service,FrameworkServlet.service,HttpServlet.service处理各种控制请求。控制请求分析,请参考SpringMvc章节。
- public abstract class HttpServlet extends GenericServlet {
- //...
- @Override
- public void service(ServletRequest req, ServletResponse res)
- throws ServletException, IOException
- {
- HttpServletRequest request;
- HttpServletResponse response;
-
- if (!(req instanceof HttpServletRequest &&
- res instanceof HttpServletResponse)) {
- throw new ServletException("non-HTTP request or response");
- }
-
- request = (HttpServletRequest) req;
- response = (HttpServletResponse) res;
-
- service(request, response);
- }
-
- protected void service(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException
- {
- String method = req.getMethod();
-
- if (method.equals(METHOD_GET)) {
- long lastModified = getLastModified(req);
- if (lastModified == -1) {
- // servlet doesn't support if-modified-since, no reason
- // to go through further expensive logic
- doGet(req, resp);
- } else {
- long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
- if (ifModifiedSince < lastModified) {
- // If the servlet mod time is later, call doGet()
- // Round down to the nearest second for a proper compare
- // A ifModifiedSince of -1 will always be less
- maybeSetLastModified(resp, lastModified);
- doGet(req, resp);
- } else {
- resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
- }
- }
-
- } else if (method.equals(METHOD_HEAD)) {
- long lastModified = getLastModified(req);
- maybeSetLastModified(resp, lastModified);
- doHead(req, resp);
-
- } else if (method.equals(METHOD_POST)) {
- doPost(req, resp);
-
- } else if (method.equals(METHOD_PUT)) {
- doPut(req, resp);
-
- } else if (method.equals(METHOD_DELETE)) {
- doDelete(req, resp);
-
- } else if (method.equals(METHOD_OPTIONS)) {
- doOptions(req,resp);
-
- } else if (method.equals(METHOD_TRACE)) {
- doTrace(req,resp);
-
- } else {
- //
- // Note that this means NO servlet supports whatever
- // method was requested, anywhere on this server.
- //
-
- String errMsg = lStrings.getString("http.method_not_implemented");
- Object[] errArgs = new Object[1];
- errArgs[0] = method;
- errMsg = MessageFormat.format(errMsg, errArgs);
-
- resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
- }
- }
- //...
- }
- public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
- //...
- @Override
- protected final void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- processRequest(request, response);
- }
- //...
- }
SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain:
- public class FilterChainProxy extends GenericFilterBean {
- private static final class VirtualFilterChain implements FilterChain {
- private final List<Filter> additionalFilters;
- }
- }
其中additionalFilters的值为:
该过滤器处理路径为/logout的请求【GET、POST、PUT、DELETE】。
该过滤器处理路径为/oauth2/authorize的请求。
- public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter {
- // ...
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- Authentication authenticationRequest = this.authenticationConverter.convert(request);
- if (authenticationRequest instanceof AbstractAuthenticationToken) {
- ((AbstractAuthenticationToken) authenticationRequest).setDetails(
- this.authenticationDetailsSource.buildDetails(request));
- }
- if (authenticationRequest != null) {
- validateClientIdentifier(authenticationRequest);
- Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
- this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult);
- }
- filterChain.doFilter(request, response);
- }
- // ...
- }
1、this.authenticationConverter.convert中converters的值为:
该过滤器处理路径为【post】:/oauth2/token,/oauth2/introspect,/oauth2/revoke的请求。
客户端认证 OAuth2ClientAuthenticationFilter
- // OAuth2ClientAuthenticationFilter
- public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter {
- // ...
- // authenticationConverter 为 DelegatingAuthenticationConverter
- // 登录时带Authorization返回的是 ClientSecretBasicAuthenticationConverter
- // 登录成功后带jwt,返回的是 JwtClientAssertionAuthenticationConverter
- Authentication authenticationRequest = this.authenticationConverter.convert(request);
- Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
- // ...
- }
-
- public final class DelegatingAuthenticationConverter implements AuthenticationConverter {
- // ...
- public Authentication convert(HttpServletRequest request) {
- Assert.notNull(request, "request cannot be null");
- // converters 有四个:
- // JwtClientAssertionAuthenticationConverter,处理JWT
- // ClientSecretBasicAuthenticationConverter,处理头部Authorization
- // ClientSecretPostAuthenticationConverter,处理参数client_id和client_secret
- // PublicClientAuthenticationConverter,PKCE,grant_type,code,code_verifier
- for (AuthenticationConverter converter : this.converters) {
- Authentication authentication = converter.convert(request);
- if (authentication != null) {
- return authentication;
- }
- }
- return null;
- }
- }
通过AuthenticationConverter创建相应的Authentication,然后调用ProviderManager类进行认证:
- public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
- // ...
- @Override
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- // getProviders() 的值为:
- // AnonymousAuthenticationProvider
- // JwtClientAssertionAuthenticationProvider@19312
- // ClientSecretAuthenticationProvider@19402
- // PublicClientAuthenticationProvider@19406
- // OAuth2AuthorizationCodeRequestAuthenticationProvider@19407
- // ...
- for (AuthenticationProvider provider : getProviders()) {
- if (!provider.supports(toTest)) {
- continue;
- }
- result = provider.authenticate(authentication);
- if (result != null) {
- copyDetails(authentication, result);
- break;
- }
- }
- }
- // ...
- }
1、getProviders()的值为:
未登陆时,AuthenticationConverter为ClientSecretBasicAuthenticationConverter,AuthenticationProvider为:ClientSecretAuthenticationProvider。
处理表单提交的身份验证。必须提供两个参数:用户名和密码。
- public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
- // ...
- @Override
- public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
- throws AuthenticationException {
- if (this.postOnly && !request.getMethod().equals("POST")) {
- throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
- }
- String username = obtainUsername(request);
- username = (username != null) ? username.trim() : "";
- String password = obtainPassword(request);
- password = (password != null) ? password : "";
- UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
- password);
- // Allow subclasses to set the "details" property
- setDetails(request, authRequest);
- return this.getAuthenticationManager().authenticate(authRequest);
- }
- // ...
- }
匿名验证过滤器,前面的验证没有通过,则采用匿名验证
- public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
- // ...
- @Override
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
- throws IOException, ServletException {
- if (SecurityContextHolder.getContext().getAuthentication() == null) {
- Authentication authentication = createAuthentication((HttpServletRequest) req);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
- context.setAuthentication(authentication);
- SecurityContextHolder.setContext(context);
- if (this.logger.isTraceEnabled()) {
- this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
- + SecurityContextHolder.getContext().getAuthentication()));
- }
- else {
- this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
- }
- }
- chain.doFilter(req, res);
- }
- // ...
- }
该过滤器处理路径为【post】:/oauth2/token的请求。
令牌颁发 OAuth2TokenEndpointFilter
- public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
- // ...
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- // ==> 1
- Authentication authorizationGrantAuthentication = this.authenticationConverter.convert(request);
- OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
- (OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication);
- this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, accessTokenAuthentication);
- }
- // ...
- }
1、this.authenticationConverter.convert中converters的值为:
其中的DelegatingAuthenticationConverter中converters的值为:
未登陆时,AuthenticationConverter为DelegatingAuthenticationConverter中的OAuth2ResourceOwnerPasswordAuthenticationConverter,AuthenticationProvider为:OAuth2ResourceOwnerPasswordAuthenticationProvider。
SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain,VirtualFilterChain中additionalFilters的值为:
- public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
- // ...
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- String token;
- try {
- // bearerTokenResolver值为 ==> PigBearerTokenExtractor
- token = this.bearerTokenResolver.resolve(request);
- }
- catch (OAuth2AuthenticationException invalid) {
- this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
- this.authenticationEntryPoint.commence(request, response, invalid);
- return;
- }
- if (token == null) {
- this.logger.trace("Did not process request since did not find bearer token");
- filterChain.doFilter(request, response);
- return;
- }
-
- BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
- authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
-
- try {
- // authenticationManager类型为ProviderManager,其内有两个Provider:
- // AnonymousAuthenticationProvider 和 OpaqueTokenAuthenticationProvider
- AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
- Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
- context.setAuthentication(authenticationResult);
- SecurityContextHolder.setContext(context);
- this.securityContextRepository.saveContext(context, request, response);
- if (this.logger.isDebugEnabled()) {
- this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authenticationResult));
- }
- filterChain.doFilter(request, response);
- }
- catch (AuthenticationException failed) {
- SecurityContextHolder.clearContext();
- this.logger.trace("Failed to process authentication request", failed);
- this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);
- }
- }
- // ...
- }
PigBearerTokenExtractor 用来过滤,以及获取token,Authorization头,以Bearer开头。
Authorization: Bearer 279dc4ec-db03-4d21-a849-6f3acf750b12
- public class PigBearerTokenExtractor implements BearerTokenResolver {
- // ...
- @Override
- public String resolve(HttpServletRequest request) {
- boolean match = urlProperties.getUrls().stream()
- .anyMatch(url -> pathMatcher.match(url, request.getRequestURI()));
-
- if (match) {
- return null;
- }
-
- final String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
- final String parameterToken = isParameterTokenSupportedForRequest(request)
- ? resolveFromRequestParameters(request) : null;
- if (authorizationHeaderToken != null) {
- if (parameterToken != null) {
- final BearerTokenError error = BearerTokenErrors
- .invalidRequest("Found multiple bearer tokens in the request");
- throw new OAuth2AuthenticationException(error);
- }
- return authorizationHeaderToken;
- }
- if (parameterToken != null && isParameterTokenEnabledForRequest(request)) {
- return parameterToken;
- }
- return null;
- }
-
- private String resolveFromAuthorizationHeader(HttpServletRequest request) {
- String authorization = request.getHeader(this.bearerTokenHeaderName);
- if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
- return null;
- }
- Matcher matcher = authorizationPattern.matcher(authorization);
- if (!matcher.matches()) {
- BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
- throw new OAuth2AuthenticationException(error);
- }
- return matcher.group("token");
- }
- // ...
- }
BearerTokenAuthenticationFilter中构建BearerTokenAuthenticationToken,然后调用OpaqueTokenAuthenticationProvider进行认证。
- public final class OpaqueTokenAuthenticationProvider implements AuthenticationProvider {
- //...
- @Override
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- if (!(authentication instanceof BearerTokenAuthenticationToken)) {
- return null;
- }
- BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
- // principal类型为PigUser
- // public class PigUser extends User implements OAuth2AuthenticatedPrincipal
- OAuth2AuthenticatedPrincipal principal = getOAuth2AuthenticatedPrincipal(bearer);
- AbstractAuthenticationToken result = convert(principal, bearer.getToken());
- result.setDetails(bearer.getDetails());
- this.logger.debug("Authenticated token");
- return result;
- }
- private OAuth2AuthenticatedPrincipal getOAuth2AuthenticatedPrincipal(BearerTokenAuthenticationToken bearer) {
- try {
- // introspector值为 ==> PigCustomOpaqueTokenIntrospector
- return this.introspector.introspect(bearer.getToken());
- }
- catch (BadOpaqueTokenException failed) {
- this.logger.debug("Failed to authenticate since token was invalid");
- throw new InvalidBearerTokenException(failed.getMessage(), failed);
- }
- catch (OAuth2IntrospectionException failed) {
- throw new AuthenticationServiceException(failed.getMessage(), failed);
- }
- }
- //...
- }
- public class PigCustomOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
- private final OAuth2AuthorizationService authorizationService;
- @Override
- public OAuth2AuthenticatedPrincipal introspect(String token) {
- // authorizationService 值为 PigRedisOAuth2AuthorizationService
- OAuth2Authorization oldAuthorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
- if (Objects.isNull(oldAuthorization)) {
- throw new InvalidBearerTokenException(token);
- }
-
- // 客户端模式默认返回
- if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(oldAuthorization.getAuthorizationGrantType())) {
- return new PigClientCredentialsOAuth2AuthenticatedPrincipal(oldAuthorization.getAttributes(),
- AuthorityUtils.NO_AUTHORITIES, oldAuthorization.getPrincipalName());
- }
-
- Map<String, PigUserDetailsService> userDetailsServiceMap = SpringUtil
- .getBeansOfType(PigUserDetailsService.class);
-
- Optional<PigUserDetailsService> optional = userDetailsServiceMap.values().stream()
- .filter(service -> service.support(Objects.requireNonNull(oldAuthorization).getRegisteredClientId(),
- oldAuthorization.getAuthorizationGrantType().getValue()))
- .max(Comparator.comparingInt(Ordered::getOrder));
-
- UserDetails userDetails = null;
- try {
- Object principal = Objects.requireNonNull(oldAuthorization).getAttributes().get(Principal.class.getName());
- UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) principal;
- Object tokenPrincipal = usernamePasswordAuthenticationToken.getPrincipal();
- userDetails = optional.get().loadUserByUser((PigUser) tokenPrincipal);
- }
- catch (UsernameNotFoundException notFoundException) {
- log.warn("用户不不存在 {}", notFoundException.getLocalizedMessage());
- throw notFoundException;
- }
- catch (Exception ex) {
- log.error("资源服务器 introspect Token error {}", ex.getLocalizedMessage());
- }
- return (PigUser) userDetails;
- }
- }
introspect函数从PigRedisOAuth2AuthorizationService通过token获取OAuth2Authorization,然后再根据用户信息,去数据库中loadUserByUser获取用户信息(缓存中有则从缓存中获取)。
- public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
- //...
- public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
- Assert.hasText(token, "token cannot be empty");
- Assert.notNull(tokenType, "tokenType cannot be empty");
- redisTemplate.setValueSerializer(RedisSerializer.java());
- // 从 Redis中获取认证信息
- return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
- }
- //...
- }
登陆后,后调用BearerTokenAuthenticationFilter过滤器进行认证。
- @SysLog("添加部门")
- @PostMapping
- @PreAuthorize("@pms.hasPermission('sys_dept_add')")
- public R<Boolean> save(@Valid @RequestBody SysDept sysDept) {
- return R.ok(sysDeptService.saveDept(sysDept));
- }
SpringBoot - @PreAuthorize注解详解
使用AOP进行切面代理
- DispatcherServlet::doService(HttpServletRequest request, HttpServletResponse response)
- // ==>
- DispatcherServlet::doDispatch(HttpServletRequest request, HttpServletResponse response)
- void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- }
- // ==>
- AbstractHandlerMethodAdapter::handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- // ==>
- RequestMappingHandlerAdapter::handleInternal(HttpServletRequest request,
- HttpServletResponse response, HandlerMethod handlerMethod)
- // ==>
- RequestMappingHandlerAdapter::invokeHandlerMethod(HttpServletRequest request,
- HttpServletResponse response, HandlerMethod handlerMethod)
- // ==>
- ServletInvocableHandlerMethod::invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
- Object... providedArgs)
- public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
- Object... providedArgs) throws Exception {
- // 调用函数方法
- Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
- // 如果有返回值,则处理返回值
- this.returnValueHandlers.handleReturnValue(
- returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
- }
- // ==>
- InvocableHandlerMethod::invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
- Object... providedArgs)
- public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
- Object... providedArgs) throws Exception {
- // 获取函数参数
- Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
- return doInvoke(args);
- }
- // ==>
- InvocableHandlerMethod::doInvoke(Object... args)
- // ==>
- Method::invoke(Object obj, Object... args)
- // ==>
- DelegatingMethodAccessorImpl::invoke(Object obj, Object[] args)
- // ==>
- NativeMethodAccessorImpl::invoke(Object obj, Object[] args)
- // ==>
- CglibAopProxy::intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
- // ==>
- CglibAopProxy::proceed()
- // ==>
- ReflectiveMethodInvocation::proceed()
- // ==>
- ExposeInvocationInterceptor::invoke(MethodInvocation mi)
- // ==>
- CglibAopProxy::proceed()
- // ==>
- ReflectiveMethodInvocation::proceed()
- // ==>
- MethodSecurityInterceptor::invoke(MethodInvocation mi)
- // ==>
- AbstractSecurityInterceptor::beforeInvocation(Object object)
- // ==>
- AbstractSecurityInterceptor::attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
- Authentication authenticated)
- private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
- Authentication authenticated) {
- // accessDecisionManager类型为AffirmativeBased
- this.accessDecisionManager.decide(authenticated, object, attributes);
- }
- // ==>
- AffirmativeBased::decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
- public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
- throws AccessDeniedException {
- int deny = 0;
- // getDecisionVoters()即decisionVoters,其值为:
- // PreInvocationAuthorizationAdviceVoter
- // RoleVoter
- // AuthenticatedVoter
- for (AccessDecisionVoter voter : getDecisionVoters()) {
- int result = voter.vote(authentication, object, configAttributes);
- switch (result) {
- case AccessDecisionVoter.ACCESS_GRANTED:
- return;
- case AccessDecisionVoter.ACCESS_DENIED:
- deny++;
- break;
- default:
- break;
- }
- }
- if (deny > 0) {
- throw new AccessDeniedException(
- this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
- }
- // To get this far, every AccessDecisionVoter abstained
- checkAllowIfAllAbstainDecisions();
- }
AffirmativeBased::decide调用各个投票器,进行投票。没有通过则抛出AccessDeniedException异常。
- // ==>
- PreInvocationAuthorizationAdviceVoter::vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes)
- public int vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes) {
- // Find prefilter and preauth (or combined) attributes
- // if both null, abstain else call advice with them
- PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
- if (preAttr == null) {
- // No expression based metadata, so abstain
- return ACCESS_ABSTAIN;
- }
- // ACCESS_GRANTED 赞成,ACCESS_ABSTAIN 弃权,ACCESS_DENIED 拒绝
- return this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED;
- }
- // ==>
- ExpressionBasedPreInvocationAdvice::before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr)
- // ==>
- ExpressionUtils::evaluateAsBoolean(Expression expr, EvaluationContext ctx)
- // ==>
- SpelExpression::getValue(EvaluationContext context, @Nullable Class<T> expectedResultType)
- // ==>
- SpelNodeImpl::getTypedValue(ExpressionState expressionState)
- // ==>
- CompoundExpression::getValueInternal(ExpressionState state)
- // ==>
- MethodReference::getValue()
- // ==>
- MethodReference::getValueInternal(EvaluationContext evaluationContext,
- @Nullable Object value, @Nullable TypeDescriptor targetType, Object[] arguments)
- // ==>
- ReflectiveMethodExecutor::execute(EvaluationContext context, Object target, Object... arguments)
- // ==>
- Method::invoke(Object obj, Object... args)
- // ==>
- NativeMethodAccessorImpl::invoke(Object obj, Object[] args)
- // ==>
- PermissionService::hasPermission(String... permissions)
- // permissions的值为 sys_dept_add
最终调用到了PermissionService::hasPermission
- public class PermissionService {
- /**
- * 判断接口是否有任意xxx,xxx权限
- * @param permissions 权限
- * @return {boolean}
- */
- public boolean hasPermission(String... permissions) {
- if (ArrayUtil.isEmpty(permissions)) {
- return false;
- }
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- if (authentication == null) {
- return false;
- }
- Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
- return authorities.stream().map(GrantedAuthority::getAuthority).filter(StringUtils::hasText)
- .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));
- }
- }
- public final class ApplicationFilterChain implements FilterChain {
- // ...
- private ApplicationFilterConfig[] filters
- private void internalDoFilter(ServletRequest request,
- ServletResponse response)
- throws IOException, ServletException {
-
- // Call the next filter if there is one
- if (pos < n) {
- ApplicationFilterConfig filterConfig = filters[pos++];
- try {
- Filter filter = filterConfig.getFilter();
- filter.doFilter(request, response, this);
- }
- return;
- }
- // ...
- // We fell off the end of the chain -- call the servlet instance
- try {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(request);
- lastServicedResponse.set(response);
- }
-
- if (request.isAsyncSupported() && !servletSupportsAsync) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
- Boolean.FALSE);
- }
- // Use potentially wrapped request from this point
- if ((request instanceof HttpServletRequest) &&
- (response instanceof HttpServletResponse) &&
- Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal =
- ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[]{req, res};
- SecurityUtil.doAsPrivilege("service",
- servlet,
- classTypeUsedInService,
- args,
- principal);
- } else {
- servlet.service(request, response);
- }
- } catch (IOException | ServletException | RuntimeException e) {
- throw e;
- } catch (Throwable e) {
- e = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(e);
- throw new ServletException(sm.getString("filterChain.servlet"), e);
- } finally {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(null);
- lastServicedResponse.set(null);
- }
- }
- }
- // ...
- }
ApplicationFilterChain有filters对象,其是ApplicationFilterConfig数组,ApplicationFilterChain会依次执行ApplicationFilterChain里的Filter对象,依次为:OrderedCharacterEncodingFilter,OrderedFormContentFilter,OrderedRequestContextFilter,DelegatingFilterProxyRegistrationBean$1。过滤器链执行完毕后将调用servlet.service(request, response) 对servlet请求进行处理,参见SpringMvc章节。
编码转换过滤器?
ServletRequestAttributes?
- public class DelegatingFilterProxy extends GenericFilterBean {
- private volatile Filter delegate;
- //...
- protected void invokeDelegate(
- Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- delegate.doFilter(request, response, filterChain);
- }
- //...
- }
DelegatingFilterProxy中含有delegate对象,其值为FilterChainProxy,其会执行代理对象中的过滤链,详细分析见FilterChainProxy段。
SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain,VirtualFilterChain中additionalFilters的值为:
- public class BasicAuthenticationFilter extends OncePerRequestFilter {
- //...
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
- throws IOException, ServletException {
- try {
- // authenticationConverter 类型为 BasicAuthenticationConverter
- UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
- if (authRequest == null) {
- this.logger.trace("Did not process authentication request since failed to find "
- + "username and password in Basic Authorization header");
- chain.doFilter(request, response);
- return;
- }
- String username = authRequest.getName();
- this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
- if (authenticationIsRequired(username)) {
- // 开始认证,authenticationManager中含有两个Provider
- // AnonymousAuthenticationProvider
- // DaoAuthenticationProvider
- Authentication authResult = this.authenticationManager.authenticate(authRequest);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
- context.setAuthentication(authResult);
- SecurityContextHolder.setContext(context);
- if (this.logger.isDebugEnabled()) {
- this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
- }
- this.rememberMeServices.loginSuccess(request, response, authResult);
- this.securityContextRepository.saveContext(context, request, response);
- onSuccessfulAuthentication(request, response, authResult);
- }
- }
- catch (AuthenticationException ex) {
- SecurityContextHolder.clearContext();
- this.logger.debug("Failed to process authentication request", ex);
- this.rememberMeServices.loginFail(request, response);
- onUnsuccessfulAuthentication(request, response, ex);
- if (this.ignoreFailure) {
- chain.doFilter(request, response);
- }
- else {
- this.authenticationEntryPoint.commence(request, response, ex);
- }
- return;
- }
-
- chain.doFilter(request, response);
- }
- //...
- }
BasicAuthenticationConverter调用convert(HttpServletRequest request)获得UsernamePasswordAuthenticationToken。解析头字段为Authorization的值,其为Base64加密,解析后值为mall-admin:123456。
Authorization: Basic bWFsbC1hZG1pbjoxMjM0NTY=
- public class BasicAuthenticationConverter implements AuthenticationConverter {
- //...
- public UsernamePasswordAuthenticationToken convert(HttpServletRequest request) {
- String header = request.getHeader(HttpHeaders.AUTHORIZATION);
- if (header == null) {
- return null;
- }
- header = header.trim();
- if (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {
- return null;
- }
- if (header.equalsIgnoreCase(AUTHENTICATION_SCHEME_BASIC)) {
- throw new BadCredentialsException("Empty basic authentication token");
- }
- byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
- byte[] decoded = decode(base64Token);
- String token = new String(decoded, getCredentialsCharset(request));
- int delim = token.indexOf(":");
- if (delim == -1) {
- throw new BadCredentialsException("Invalid basic authentication token");
- }
- UsernamePasswordAuthenticationToken result = UsernamePasswordAuthenticationToken
- .unauthenticated(token.substring(0, delim), token.substring(delim + 1));
- result.setDetails(this.authenticationDetailsSource.buildDetails(request));
- return result;
- }
- //...
- }
- public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
- //...
- // authenticate此段代码在AbstractUserDetailsAuthenticationProvider类中
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- // 从缓存中找,没有启用缓存则为空
- UserDetails user = this.userCache.getUserFromCache(username);
- if (user == null) {
- // 最后调用 JdbcClientDetailsService中loadClientByClientId方法
- user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
- }
- }
- //...
- }
- public class JdbcClientDetailsService implements ClientDetailsService, ClientRegistrationService {
- public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
- ClientDetails details;
- try {
- details = jdbcTemplate.queryForObject(selectClientDetailsSql, new ClientDetailsRowMapper(), clientId);
- }
- catch (EmptyResultDataAccessException e) {
- throw new NoSuchClientException("No client with requested id: " + clientId);
- }
-
- return details;
- }
- }
details值为:
最终DaoAuthenticationProvider进行了认证,即BasicAuthenticationFilter响应了认证。
SpringSecurity中FilterChainProxy类有内部类VirtualFilterChain,VirtualFilterChain中additionalFilters的值为:
- public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter {
- // ...
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- String token;
- try {
- // bearerTokenResolver值为 ==> DefaultBearerTokenResolver
- token = this.bearerTokenResolver.resolve(request);
- }
- catch (OAuth2AuthenticationException invalid) {
- this.logger.trace("Sending to authentication entry point since failed to resolve bearer token", invalid);
- this.authenticationEntryPoint.commence(request, response, invalid);
- return;
- }
- if (token == null) {
- this.logger.trace("Did not process request since did not find bearer token");
- filterChain.doFilter(request, response);
- return;
- }
-
- BearerTokenAuthenticationToken authenticationRequest = new BearerTokenAuthenticationToken(token);
- authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
-
- try {
- // authenticationManager类型为ProviderManager,其内有两个Provider:
- // AnonymousAuthenticationProvider 和 JwtAuthenticationProvider
- AuthenticationManager authenticationManager = this.authenticationManagerResolver.resolve(request);
- Authentication authenticationResult = authenticationManager.authenticate(authenticationRequest);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
- context.setAuthentication(authenticationResult);
- SecurityContextHolder.setContext(context);
- this.securityContextRepository.saveContext(context, request, response);
- if (this.logger.isDebugEnabled()) {
- this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authenticationResult));
- }
- filterChain.doFilter(request, response);
- }
- catch (AuthenticationException failed) {
- SecurityContextHolder.clearContext();
- this.logger.trace("Failed to process authentication request", failed);
- this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);
- }
- }
- // ...
- }
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
- public final class DefaultBearerTokenResolver implements BearerTokenResolver {
- // ...
- @Override
- public String resolve(final HttpServletRequest request) {
- final String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
- final String parameterToken = isParameterTokenSupportedForRequest(request)
- ? resolveFromRequestParameters(request) : null;
- if (authorizationHeaderToken != null) {
- if (parameterToken != null) {
- final BearerTokenError error = BearerTokenErrors
- .invalidRequest("Found multiple bearer tokens in the request");
- throw new OAuth2AuthenticationException(error);
- }
- return authorizationHeaderToken;
- }
- if (parameterToken != null && isParameterTokenEnabledForRequest(request)) {
- return parameterToken;
- }
- return null;
- }
-
- private String resolveFromAuthorizationHeader(HttpServletRequest request) {
- String authorization = request.getHeader(this.bearerTokenHeaderName);
- if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
- return null;
- }
- Matcher matcher = authorizationPattern.matcher(authorization);
- if (!matcher.matches()) {
- BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
- throw new OAuth2AuthenticationException(error);
- }
- return matcher.group("token");
- }
- // ...
- }
- public final class JwtAuthenticationProvider implements AuthenticationProvider {
- // ...
- public Authentication authenticate(Authentication authentication) throws AuthenticationException {
- BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
- Jwt jwt = getJwt(bearer);
- // jwtAuthenticationConverter 类型为 JwtAuthenticationConverter
- AbstractAuthenticationToken token = this.jwtAuthenticationConverter.convert(jwt);
- token.setDetails(bearer.getDetails());
- this.logger.debug("Authenticated token");
- return token;
- }
-
- private Jwt getJwt(BearerTokenAuthenticationToken bearer) {
- try {
- return this.jwtDecoder.decode(bearer.getToken());
- }
- catch (BadJwtException failed) {
- this.logger.debug("Failed to authenticate since the JWT was invalid");
- throw new InvalidBearerTokenException(failed.getMessage(), failed);
- }
- catch (JwtException failed) {
- throw new AuthenticationServiceException(failed.getMessage(), failed);
- }
- }
- // ...
- }
getJwt函数中解密了token字段,Jwt jwt的值为:
- public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
- // ...
- public final AbstractAuthenticationToken convert(Jwt jwt) {
- Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
-
- String principalClaimValue = jwt.getClaimAsString(this.principalClaimName);
- return new JwtAuthenticationToken(jwt, authorities, principalClaimValue);
- }
- // ...
- }
实现了JWT认证。
- public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
- // ...
- public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
- throws IOException, ServletException {
- if (SecurityContextHolder.getContext().getAuthentication() == null) {
- Authentication authentication = createAuthentication((HttpServletRequest) req);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
- context.setAuthentication(authentication);
- SecurityContextHolder.setContext(context);
- if (this.logger.isTraceEnabled()) {
- this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
- + SecurityContextHolder.getContext().getAuthentication()));
- }
- else {
- this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
- }
- }
- else {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace(LogMessage.of(() -> "Did not set SecurityContextHolder since already authenticated "
- + SecurityContextHolder.getContext().getAuthentication()));
- }
- }
- chain.doFilter(req, res);
- }
- // ...
- }
创建匿名认证
- @ApiOperation(value = "新增用户")
- @PostMapping
- @PreAuthorize("@pms.hasPermission('sys:user:add')")
- public Result saveUser(
- @Validate @RequestBody UserForm userForm
- ) {
- boolean result = userService.saveUser(userForm);
- return Result.judge(result);
- }
- @Service("pms")
- @RequiredArgsConstructor
- public class PermissionService {
- private final RedisTemplate redisTemplate;
- public boolean hasPermission(String perm) {
- if (StrUtil.isBlank(perm)) {
- return false;
- }
-
- if (SecurityUtils.isRoot()) {
- return true;
- }
-
- Long userId = SecurityUtils.getUserId();
- Set<String> perms = (Set<String>) redisTemplate.opsForValue().get("AUTH:USER_PERMS:" + userId);
- if (CollectionUtil.isEmpty(perms)) {
- return false;
- }
- return perms.stream().anyMatch(item -> PatternMatchUtils.simpleMatch(perm, item));
- }
- }
-
- public class SecurityUtils {
- public static Long getUserId() {
- Long userId = Convert.toLong(getTokenAttributes().get("userId"));
- return userId;
- }
- public static Map<String, Object> getTokenAttributes() {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- if (authentication != null) {
- if (authentication instanceof JwtAuthenticationToken) {
- JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) authentication;
- Map<String, Object> tokenAttributes = jwtAuthenticationToken.getTokenAttributes();
- return tokenAttributes;
- }
- }
- return Collections.EMPTY_MAP;
- }
- }
jwtAuthenticationToken.getTokenAttributes()
- public class JwtAuthenticationToken extends AbstractOAuth2TokenAuthenticationToken<Jwt> {
- //...
- public Map<String, Object> getTokenAttributes() {
- return this.getToken().getClaims();
- }
- // token中保存的为JWT对象
- public final T getToken() {
- return this.token;
- }
- //...
- }
-
- public class Jwt extends AbstractOAuth2Token implements JwtClaimAccessor {
- //...
- private final Map<String, Object> claims;
- public Map<String, Object> getClaims() {
- return this.claims;
- }
- //...
- }
claims中保存的值为:
- spring:
- security:
- oauth2:
- resourceserver:
- jwt:
- jwk-set-uri: http://localhost:9999/youlai-auth/rsa/publicKey
此处相关的处理代码在org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties中。
- @ConfigurationProperties(prefix = "spring.security.oauth2.resourceserver")
- public class OAuth2ResourceServerProperties {
- //...
- private final Jwt jwt = new Jwt();
- public static class Jwt {
-
- /**
- * JSON Web Key URI to use to verify the JWT token.
- */
- private String jwkSetUri;
- private Resource publicKeyLocation;
- //...
- }
- //...
- }
pom中引入了:
- <!-- OAuth2 认证服务器-->
- <dependency>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- </dependency>
org/springframework/boot/autoconfigure/security/oauth2/authserver下有:
AuthorizationServerProperties.java,AuthorizationServerTokenServicesConfiguration.java,OAuth2AuthorizationServerConfiguration.java三个文件。
pom中引入了:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
- </dependency>
resource下有:OAuth2ResourceServerConfiguration 配置类
com.youlai.common.security.config.ResourceServerConfig
- @ConfigurationProperties(prefix = "security")
- @Configuration
- @EnableWebSecurity
- @EnableGlobalMethodSecurity(prePostEnabled = true)
- @Slf4j
- @RequiredArgsConstructor
- public class ResourceServerConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
-
- if (CollectionUtil.isEmpty(ignoreUrls)) {
- ignoreUrls = Arrays.asList("/webjars/**", "/doc.html", "/swagger-resources/**", "/v2/api-docs");
- }
-
- log.info("whitelist path:{}", JSONUtil.toJsonStr(ignoreUrls));
-
- http
- .csrf().disable()
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and()
- .authorizeRequests()
- .antMatchers(Convert.toStrArray(ignoreUrls)).permitAll()
- .anyRequest().authenticated()
- ;
- http.oauth2ResourceServer()
- .jwt()
- .jwtAuthenticationConverter(jwtAuthenticationConverter())
- .and()
- .authenticationEntryPoint(authenticationEntryPoint)
- .accessDeniedHandler(accessDeniedHandler)
- ;
- return http.build();
- }
- //...
- }
http.oauth2ResourceServer().jwt()中默认创建的就是BearerTokenAuthenticationFilter。
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder
- public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
- extends AbstractSecurityBuilder<O> {
- private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers;
- }
AbstractConfiguredSecurityBuilder中configurers决定了创建什么类型的filter。
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration
- public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>>
- extends AbstractHttpConfigurer<HttpBasicConfigurer<B>, B> {
- //...
- @Override
- public void configure(B http) {
- AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
- BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager,
- this.authenticationEntryPoint);
- if (this.authenticationDetailsSource != null) {
- basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
- }
- RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
- if (rememberMeServices != null) {
- basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
- }
- basicAuthenticationFilter = postProcess(basicAuthenticationFilter);
- http.addFilter(basicAuthenticationFilter);
- }
- }
HttpBasicConfigurer在configure函数中创建BasicAuthenticationFilter。
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration
org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer
- public final class OAuth2AuthorizationServerConfigurer
- extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer, HttpSecurity> {
- //...
- public void configure(HttpSecurity httpSecurity) {
- this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
-
- AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
-
- AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(authorizationServerSettings);
- httpSecurity.addFilterAfter(postProcess(authorizationServerContextFilter), SecurityContextHolderFilter.class);
-
- JWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);
- if (jwkSource != null) {
- NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(
- jwkSource, authorizationServerSettings.getJwkSetEndpoint());
- httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
- }
- }
- //...
- }
OAuth2ClientAuthenticationFilter ?
org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerConfiguration
- public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<H>>
- extends AbstractHttpConfigurer<OAuth2ResourceServerConfigurer<H>, H> {
- //...
- public void configure(H http) {
- BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
- this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
- AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
- if (resolver == null) {
- AuthenticationManager authenticationManager = getAuthenticationManager(http);
- resolver = (request) -> authenticationManager;
- }
-
- BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
- filter.setBearerTokenResolver(bearerTokenResolver);
- filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
- filter = postProcess(filter);
- http.addFilter(filter);
- }
- //...
- }
OAuth2ResourceServerConfigurer在configure函数中创建BearerTokenAuthenticationFilter。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。