鉴权服务器对客户端鉴权之后,会生成token,客户端使用token,就可以去资源服务器获取资源。
@EnableResourceServer
@Import(ResourceServerConfiguration.class)
public @interface EnableResourceServer {
}
ResourceServerConfiguration
ResourceServerConfiguration是WebSecurityConfigurerAdapter的子类实现。引入了ResourceServerSecurityConfigurer和ResourceServerTokenServices。
@Override
protected void configure(HttpSecurity http) throws Exception {
//引入Filter配置
ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
//引入ResourceServerTokenServices
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);
}
//匿名验证,
http.authenticationProvider(new AnonymousAuthenticationProvider("default"))
// 异常处理
.exceptionHandling()
.accessDeniedHandler(resources.getAccessDeniedHandler()).and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.csrf().disable();
//filter chain
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()) {
//所有请求需授权
http.authorizeRequests().anyRequest().authenticated();
}
}
ResourceServerSecurityConfigurer
ResourceServerSecurityConfigurer配置了:OAuth2AuthenticationProcessingFilter
@Override
public void configure(HttpSecurity http) throws Exception {
//如果是OAuth2AuthenticationManager,则设置tokenService等。
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()
//把OAuth2AuthenticationProcessingFilter 放在 AbstractPreAuthenticatedProcessingFilter 之前。
.addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
// @formatter:on
}
OAuth2AuthenticationManager
private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) {
OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
if (authenticationManager != null) {
if (authenticationManager instanceof OAuth2AuthenticationManager) {
oauthAuthenticationManager = (OAuth2AuthenticationManager) authenticationManager;
}
else {
return authenticationManager;
}
}
oauthAuthenticationManager.setResourceId(resourceId);
oauthAuthenticationManager.setTokenServices(resourceTokenServices(http));
oauthAuthenticationManager.setClientDetailsService(clientDetails());
return oauthAuthenticationManager;
}
OAuth2AuthenticationProcessingFilter
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 {
//提取Authentication
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 {
//设置ACCESS_TOKEN_VALUE到request,
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
//验证, authenticationManager 是 OAuth2AuthenticationManager
Authentication authResult = authenticationManager.authenticate(authentication);
if (debug) {
logger.debug("Authentication success: " + authResult);
}
eventPublisher.publishAuthenticationSuccess(authResult);
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}
catch (OAuth2Exception failed) {
SecurityContextHolder.clearContext();
........
return;
}
chain.doFilter(request, response);
}
TokenExtractor
从请求中提取Authentication
public interface TokenExtractor {
Authentication extract(HttpServletRequest request);
}
BearerTokenExtractor
TokenExtractor 的默认实现,Bearer方式,从header中提取。
protected String extractHeaderToken(HttpServletRequest request) {
Enumeration<String> headers = request.getHeaders("Authorization");
while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
String value = headers.nextElement();
//Bearer
if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
// Add this here for the auth details later. Would be better to change the signature of this method.
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
int commaIndex = authHeaderValue.indexOf(',');
if (commaIndex > 0) {
authHeaderValue = authHeaderValue.substring(0, commaIndex);
}
return authHeaderValue;
}
}
return null;
}
OAuth2AuthenticationManager
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null) {
throw new InvalidTokenException("Invalid token (token not found)");
}
//获取token,默认是一个UUID,
String token = (String) authentication.getPrincipal();
//根据token获取Authentication
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " + token);
}
//资源列表
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
}
//检测客户端信息
checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
// Guard against a cached copy of the same details
if (!details.equals(auth.getDetails())) {
// Preserve the authentication details from the one loaded by token services
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}
ResourceServerTokenServices
ResourceServerTokenServices实现加载Authentication和读取token的功能。
public interface ResourceServerTokenServices {
OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException;
OAuth2AccessToken readAccessToken(String accessToken);
}
流程:
图从其他地方copy的。