spring security OAuth2AuthenticationProcessingFilter的token认证流程解析

从@ResourceServer 注解进去可以看到导入了ResourceServerConfiguration这个类,
我们来看这个配置初始化了一些配置,后面会用到。

protected void configure(HttpSecurity http) throws Exception {
//新增一个ResourceServerSecurityConfigurer,这个是重点。
		ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
		//如果配置了多个tokenService 则取第一个
		ResourceServerTokenServices services = resolveTokenServices();
		if (services != null) {
			resources.tokenServices(services);
		}
		else {
			if (tokenStore != null) {
				resources.tokenStore(tokenStore);
			}
			else if (endpoints != null) {
				resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore());
			}
		}
		if (eventPublisher != null) {
			resources.eventPublisher(eventPublisher);
		}
		for (ResourceServerConfigurer configurer : configurers) {
			configurer.configure(resources);
		}
		// @formatter:off
		http.authenticationProvider(new AnonymousAuthenticationProvider("default"))
		// N.B. exceptionHandling is duplicated in resources.configure() so that
		// it works
		.exceptionHandling()
				.accessDeniedHandler(resources.getAccessDeniedHandler()).and()
				.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
				.csrf().disable();
		// @formatter:on
		http.apply(resources);
		if (endpoints != null) {
			// Assume we are in an Authorization Server
			http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping()));
		}
		for (ResourceServerConfigurer configurer : configurers) {
			// Delegates can add authorizeRequests() here
			configurer.configure(http);
		}
		if (configurers.isEmpty()) {
			// Add anyRequest() last as a fall back. Spring Security would
			// replace an existing anyRequest() matcher with this one, so to
			// avoid that we only add it if the user hasn't configured anything.
			http.authorizeRequests().anyRequest().authenticated();
		}
	}

ResourceServerSecurityConfigurer.configure

@Override
	public void configure(HttpSecurity http) throws Exception {
		//配置认证管理器
		AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
		//配置认证过滤器
		resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
		resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
		resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
		if (eventPublisher != null) {
			resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
		}
		if (tokenExtractor != null) {
			resourcesServerFilter.setTokenExtractor(tokenExtractor);
		}
		if (authenticationDetailsSource != null) {
			resourcesServerFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
		}
		resourcesServerFilter = postProcess(resourcesServerFilter);
		resourcesServerFilter.setStateless(stateless);

		// @formatter:off
		http
			.authorizeRequests().expressionHandler(expressionHandler)
		.and()
			.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
			.exceptionHandling()
				.accessDeniedHandler(accessDeniedHandler)
				.authenticationEntryPoint(authenticationEntryPoint);
		// @formatter:on
	}

在ResourceServer中需要对token进行校验需要走如下过滤器,我们来分析下token校验的认证过程是什么样的
OAuth2AuthenticationProcessingFilter.doFilter

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
			ServletException {

		final boolean debug = logger.isDebugEnabled();
		final HttpServletRequest request = (HttpServletRequest) req;
		final HttpServletResponse response = (HttpServletResponse) res;

		try {
			//同request中提取认证信息,TokenExtractor的具体实现
			//BearerTokenExtractor.extract方法中分为两种情况,一种是从头部
			//获取token 即调用extractHeaderToken方法获取Authorization该属
			//性,并判断是否以Bearer开头不区分大小写,头部获取不到token时将从
			//请求参数中获取,请求参数中获取的key为access_token;获取到token之
			//后将往请求中塞入如下属性request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
			//并返回token,然后交给PreAuthenticatedAuthenticationToken处理,
			//最后将token塞给Authentication 对象中的principal,返回当前对象。
			Authentication authentication = tokenExtractor.extract(request);
			
			if (authentication == null) {
				if (stateless && isAuthenticated()) {
					if (debug) {
						logger.debug("Clearing security context.");
					}
					SecurityContextHolder.clearContext();
				}
				if (debug) {
					logger.debug("No token in request, will continue chain.");
				}
			}
			else {
			//获取到token之后将token值塞给request中的一个固定属性。
			//并通过OAuth2AuthenticationDetails 类创建details;
			//改对象做的事情是详细见Ⅰ返回一个 字符串对象塞给needsDetails
		
				request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
				if (authentication instanceof AbstractAuthenticationToken) {
					AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
					needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
				}
				//这里进行认证校验,authenticationManager为ResourceServerSecurityConfigurer
				//中进行设置的为oauthAuthenticationManager,其中tokenService也都在这里配置,这里为DefaultTokenServices 
				//内部调用tokenServices.loadAuthentication(token),该方法进行认证,tokenServices有好几个实现类,
				//可以是RemoteTokenServices,也可以是UserInfoTokenServices,还可以是DefaultTokenServices。
				//或者自定义实现。
				//token校验流程可以参见Ⅱ
				Authentication authResult = authenticationManager.authenticate(authentication);

				if (debug) {
					logger.debug("Authentication success: " + authResult);
				}

//发布认证成功事件				
				eventPublisher.publishAuthenticationSuccess(authResult);

//认证成功就将解析,将信息交给SecurityContextHolder中
				SecurityContextHolder.getContext().setAuthentication(authResult);

			}
		}
		catch (OAuth2Exception failed) {
			SecurityContextHolder.clearContext();

			if (debug) {
				logger.debug("Authentication request failed: " + failed);
			}
			eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
					new PreAuthenticatedAuthenticationToken("access-token", "N/A"));

			authenticationEntryPoint.commence(request, response,
					new InsufficientAuthenticationException(failed.getMessage(), failed));

			return;
		}

		chain.doFilter(request, response);
	}

Ⅰ、OAuth2AuthenticationDetails

public OAuth2AuthenticationDetails(HttpServletRequest request) {
//从请求中拿出token
		this.tokenValue = (String) request.getAttribute(ACCESS_TOKEN_VALUE);
		//获取token类型。这边为Bearer
		this.tokenType = (String) request.getAttribute(ACCESS_TOKEN_TYPE);
		//获取反问地址
		this.remoteAddress = request.getRemoteAddr();
		//获取session,如果为空返回null,如果不为null将返回sessionid并将以上元
		//素拼接成一个字符串最后返回;remoteAddress=xxx,sessionId=<SESSION>,tokenType=xxxxxtokenValue=<TOKEN>
		HttpSession session = request.getSession(false);
		this.sessionId = (session != null) ? session.getId() : null;
		StringBuilder builder = new StringBuilder();
		if (remoteAddress!=null) {
			builder.append("remoteAddress=").append(remoteAddress);
		}
		if (builder.length()>1) {
			builder.append(", ");
		}
		if (sessionId!=null) {
			builder.append("sessionId=<SESSION>");
			if (builder.length()>1) {
				builder.append(", ");
			}
		}
		if (tokenType!=null) {
			builder.append("tokenType=").append(this.tokenType);
		}
		if (tokenValue!=null) {
			builder.append("tokenValue=<TOKEN>");
		}
		this.display = builder.toString();
	}
	//该对象重写了toString方法,即返回上面所生成的字符串
@Override
	public String toString() {
		return display;
	}

Ⅱ、DefaultTokenServices.loadAuthentication解析

public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
			InvalidTokenException {
			//读取token
		OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
		if (accessToken == null) {
			throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
		}
		//判断token是否过期
		else if (accessToken.isExpired()) {
			tokenStore.removeAccessToken(accessToken);
			throw new InvalidTokenException("Access token expired: " + accessTokenValue);
		}
		//解析token。根据你设置的tokenStore进行解析。解析完成直接返回。
		OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
		if (result == null) {
			// in case of race condition
			throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
		}
		if (clientDetailsService != null) {
			String clientId = result.getOAuth2Request().getClientId();
			try {
				clientDetailsService.loadClientByClientId(clientId);
			}
			catch (ClientRegistrationException e) {
				throw new InvalidTokenException("Client not valid: " + clientId, e);
			}
		}
		return result;
	}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值