@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ResourceServerConfiguration.class})
public @interface EnableResourceServer {
}
@Configuration
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered {
private int order = 3;
@Autowired(
required = false
)
private TokenStore tokenStore;
@Autowired(
required = false
)
private AuthenticationEventPublisher eventPublisher;
@Autowired(
required = false
)
private Map<String, ResourceServerTokenServices> tokenServices;
@Autowired
private ApplicationContext context;
private List<ResourceServerConfigurer> configurers = Collections.emptyList();
@Autowired(
required = false
)
private AuthorizationServerEndpointsConfiguration endpoints;
public ResourceServerConfiguration() {
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Autowired(
required = false
)
public void setConfigurers(List<ResourceServerConfigurer> configurers) {
this.configurers = configurers;
}
protected void configure(HttpSecurity http) throws Exception {
ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
ResourceServerTokenServices services = this.resolveTokenServices();
if (services != null) {
resources.tokenServices(services);
} else if (this.tokenStore != null) {
resources.tokenStore(this.tokenStore);
} else if (this.endpoints != null) {
resources.tokenStore(this.endpoints.getEndpointsConfigurer().getTokenStore());
}
if (this.eventPublisher != null) {
resources.eventPublisher(this.eventPublisher);
}
Iterator i$ = this.configurers.iterator();
ResourceServerConfigurer configurer;
while(i$.hasNext()) {
configurer = (ResourceServerConfigurer)i$.next();
configurer.configure(resources);
}
((HttpSecurity)((HttpSecurity)http.authenticationProvider(new AnonymousAuthenticationProvider("default")).exceptionHandling().accessDeniedHandler(resources.getAccessDeniedHandler()).and()).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()).csrf().disable();
http.apply(resources);
if (this.endpoints != null) {
http.requestMatcher(new ResourceServerConfiguration.NotOAuthRequestMatcher(this.endpoints.oauth2EndpointHandlerMapping()));
}
i$ = this.configurers.iterator();
while(i$.hasNext()) {
configurer = (ResourceServerConfigurer)i$.next();
configurer.configure(http);
}
if (this.configurers.isEmpty()) {
((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated();
}
}
private ResourceServerTokenServices resolveTokenServices() {
if (this.tokenServices != null && this.tokenServices.size() != 0) {
if (this.tokenServices.size() == 1) {
return (ResourceServerTokenServices)this.tokenServices.values().iterator().next();
} else {
if (this.tokenServices.size() == 2) {
Iterator<ResourceServerTokenServices> iter = this.tokenServices.values().iterator();
ResourceServerTokenServices one = (ResourceServerTokenServices)iter.next();
ResourceServerTokenServices two = (ResourceServerTokenServices)iter.next();
if (this.elementsEqual(one, two)) {
return one;
}
}
return (ResourceServerTokenServices)this.context.getBean(ResourceServerTokenServices.class);
}
} else {
return null;
}
}
private boolean elementsEqual(Object one, Object two) {
if (one == two) {
return true;
} else {
Object targetOne = this.findTarget(one);
Object targetTwo = this.findTarget(two);
return targetOne == targetTwo;
}
}
private Object findTarget(Object item) {
Object current = item;
while(current instanceof Advised) {
try {
current = ((Advised)current).getTargetSource().getTarget();
} catch (Exception var4) {
ReflectionUtils.rethrowRuntimeException(var4);
}
}
return current;
}
private static class NotOAuthRequestMatcher implements RequestMatcher {
private FrameworkEndpointHandlerMapping mapping;
public NotOAuthRequestMatcher(FrameworkEndpointHandlerMapping mapping) {
this.mapping = mapping;
}
public boolean matches(HttpServletRequest request) {
String requestPath = this.getRequestPath(request);
Iterator i$ = this.mapping.getPaths().iterator();
String path;
do {
if (!i$.hasNext()) {
return true;
}
path = (String)i$.next();
} while(!requestPath.startsWith(this.mapping.getPath(path)));
return false;
}
private String getRequestPath(HttpServletRequest request) {
String url = request.getServletPath();
if (request.getPathInfo() != null) {
url = url + request.getPathInfo();
}
return url;
}
}
}
package org.springframework.boot.autoconfigure.security.oauth2.resource;
import java.util.Map;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.AuthorizedUrl;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@Configuration
@Conditional({OAuth2ResourceServerConfiguration.ResourceServerCondition.class})
@ConditionalOnClass({EnableResourceServer.class, SecurityProperties.class})
@ConditionalOnWebApplication
@ConditionalOnBean({ResourceServerConfiguration.class})
@Import({ResourceServerTokenServicesConfiguration.class})
public class OAuth2ResourceServerConfiguration {
private final ResourceServerProperties resource;
public OAuth2ResourceServerConfiguration(ResourceServerProperties resource) {
this.resource = resource;
}
@Bean
@ConditionalOnMissingBean({ResourceServerConfigurer.class})
public ResourceServerConfigurer resourceServer() {
return new OAuth2ResourceServerConfiguration.ResourceSecurityConfigurer(this.resource);
}
@ConditionalOnBean({AuthorizationServerEndpointsConfiguration.class})
private static class AuthorizationServerEndpointsConfigurationBeanCondition {
private AuthorizationServerEndpointsConfigurationBeanCondition() {
}
public static boolean matches(ConditionContext context) {
Class<OAuth2ResourceServerConfiguration.AuthorizationServerEndpointsConfigurationBeanCondition> type = OAuth2ResourceServerConfiguration.AuthorizationServerEndpointsConfigurationBeanCondition.class;
Conditional conditional = (Conditional)AnnotationUtils.findAnnotation(type, Conditional.class);
StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(type);
Class[] var4 = conditional.value();
int var5 = var4.length;
for(int var6 = 0; var6 < var5; ++var6) {
Class<? extends Condition> conditionType = var4[var6];
Condition condition = (Condition)BeanUtils.instantiateClass(conditionType);
if (condition.matches(context, metadata)) {
return true;
}
}
return false;
}
}
protected static class ResourceServerCondition extends SpringBootCondition implements ConfigurationCondition {
private static final Bindable<Map<String, Object>> STRING_OBJECT_MAP = Bindable.mapOf(String.class, Object.class);
private static final String AUTHORIZATION_ANNOTATION = "org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration";
protected ResourceServerCondition() {
}
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Builder message = ConditionMessage.forCondition("OAuth ResourceServer Condition", new Object[0]);
Environment environment = context.getEnvironment();
if (!(environment instanceof ConfigurableEnvironment)) {
return ConditionOutcome.noMatch(message.didNotFind("A ConfigurableEnvironment").atAll());
} else if (this.hasOAuthClientId(environment)) {
return ConditionOutcome.match(message.foundExactly("client-id property"));
} else {
Binder binder = Binder.get(environment);
String prefix = "security.oauth2.resource.";
if (binder.bind(prefix + "jwt", STRING_OBJECT_MAP).isBound()) {
return ConditionOutcome.match(message.foundExactly("JWT resource configuration"));
} else if (binder.bind(prefix + "jwk", STRING_OBJECT_MAP).isBound()) {
return ConditionOutcome.match(message.foundExactly("JWK resource configuration"));
} else if (StringUtils.hasText(environment.getProperty(prefix + "user-info-uri"))) {
return ConditionOutcome.match(message.foundExactly("user-info-uri property"));
} else if (StringUtils.hasText(environment.getProperty(prefix + "token-info-uri"))) {
return ConditionOutcome.match(message.foundExactly("token-info-uri property"));
} else {
return ClassUtils.isPresent("org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration", (ClassLoader)null) && OAuth2ResourceServerConfiguration.AuthorizationServerEndpointsConfigurationBeanCondition.matches(context) ? ConditionOutcome.match(message.found("class").items(new Object[]{"org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration"})) : ConditionOutcome.noMatch(message.didNotFind("client ID, JWT resource or authorization server").atAll());
}
}
}
private boolean hasOAuthClientId(Environment environment) {
return StringUtils.hasLength(environment.getProperty("security.oauth2.client.client-id"));
}
}
protected static class ResourceSecurityConfigurer extends ResourceServerConfigurerAdapter {
private ResourceServerProperties resource;
public ResourceSecurityConfigurer(ResourceServerProperties resource) {
this.resource = resource;
}
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(this.resource.getResourceId());
}
public void configure(HttpSecurity http) throws Exception {
((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated();
}
}
}
@Configuration
@ConditionalOnMissingBean({AuthorizationServerEndpointsConfiguration.class})
public class ResourceServerTokenServicesConfiguration {
@Configuration
@Conditional({ResourceServerTokenServicesConfiguration.RemoteTokenCondition.class})
protected static class RemoteTokenServicesConfiguration {
protected RemoteTokenServicesConfiguration() {
}
@Configuration
@ConditionalOnMissingClass({"org.springframework.social.connect.support.OAuth2ConnectionFactory"})
@Conditional({ResourceServerTokenServicesConfiguration.NotTokenInfoCondition.class})
protected static class UserInfoTokenServicesConfiguration {
private final ResourceServerProperties sso;
private final OAuth2RestOperations restTemplate;
private final AuthoritiesExtractor authoritiesExtractor;
private final PrincipalExtractor principalExtractor;
public UserInfoTokenServicesConfiguration(ResourceServerProperties sso, UserInfoRestTemplateFactory restTemplateFactory, ObjectProvider<AuthoritiesExtractor> authoritiesExtractor, ObjectProvider<PrincipalExtractor> principalExtractor) {
this.sso = sso;
this.restTemplate = restTemplateFactory.getUserInfoRestTemplate();
this.authoritiesExtractor = (AuthoritiesExtractor)authoritiesExtractor.getIfAvailable();
this.principalExtractor = (PrincipalExtractor)principalExtractor.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean({ResourceServerTokenServices.class})
public UserInfoTokenServices userInfoTokenServices() {
UserInfoTokenServices services = new UserInfoTokenServices(this.sso.getUserInfoUri(), this.sso.getClientId());
services.setRestTemplate(this.restTemplate);
services.setTokenType(this.sso.getTokenType());
if (this.authoritiesExtractor != null) {
services.setAuthoritiesExtractor(this.authoritiesExtractor);
}
if (this.principalExtractor != null) {
services.setPrincipalExtractor(this.principalExtractor);
}
return services;
}
}
@Configuration
@ConditionalOnClass({OAuth2ConnectionFactory.class})
@Conditional({ResourceServerTokenServicesConfiguration.NotTokenInfoCondition.class})
protected static class SocialTokenServicesConfiguration {
private final ResourceServerProperties sso;
private final OAuth2ConnectionFactory<?> connectionFactory;
private final OAuth2RestOperations restTemplate;
private final AuthoritiesExtractor authoritiesExtractor;
private final PrincipalExtractor principalExtractor;
public SocialTokenServicesConfiguration(ResourceServerProperties sso, ObjectProvider<OAuth2ConnectionFactory<?>> connectionFactory, UserInfoRestTemplateFactory restTemplateFactory, ObjectProvider<AuthoritiesExtractor> authoritiesExtractor, ObjectProvider<PrincipalExtractor> principalExtractor) {
this.sso = sso;
this.connectionFactory = (OAuth2ConnectionFactory)connectionFactory.getIfAvailable();
this.restTemplate = restTemplateFactory.getUserInfoRestTemplate();
this.authoritiesExtractor = (AuthoritiesExtractor)authoritiesExtractor.getIfAvailable();
this.principalExtractor = (PrincipalExtractor)principalExtractor.getIfAvailable();
}
@Bean
@ConditionalOnBean({ConnectionFactoryLocator.class})
@ConditionalOnMissingBean({ResourceServerTokenServices.class})
public SpringSocialTokenServices socialTokenServices() {
return new SpringSocialTokenServices(this.connectionFactory, this.sso.getClientId());
}
@Bean
@ConditionalOnMissingBean({ConnectionFactoryLocator.class, ResourceServerTokenServices.class})
public UserInfoTokenServices userInfoTokenServices() {
UserInfoTokenServices services = new UserInfoTokenServices(this.sso.getUserInfoUri(), this.sso.getClientId());
services.setTokenType(this.sso.getTokenType());
services.setRestTemplate(this.restTemplate);
if (this.authoritiesExtractor != null) {
services.setAuthoritiesExtractor(this.authoritiesExtractor);
}
if (this.principalExtractor != null) {
services.setPrincipalExtractor(this.principalExtractor);
}
return services;
}
}
@Configuration
@Conditional({ResourceServerTokenServicesConfiguration.TokenInfoCondition.class})
protected static class TokenInfoServicesConfiguration {
private final ResourceServerProperties resource;
protected TokenInfoServicesConfiguration(ResourceServerProperties resource) {
this.resource = resource;
}
@Bean
public RemoteTokenServices remoteTokenServices() {
RemoteTokenServices services = new RemoteTokenServices();
services.setCheckTokenEndpointUrl(this.resource.getTokenInfoUri());
services.setClientId(this.resource.getClientId());
services.setClientSecret(this.resource.getClientSecret());
return services;
}
}
}
}
@Configuration
@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class) // 容器中没有 AuthorizationServerEndpointsConfiguration 实例
public class ResourceServerTokenServicesConfiguration {
// 注入对应的 ResourceServerTokenServices
}
// 关于资源服务器自定义配置
public class AutoResourceServerConfiguration implements ResourceServerConfigurer {
private final RemoteTokenServices remoteTokenServices;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); // 默认访问令牌转换器
UserAuthenticationConverter userTokenConverter = new EnhanceUserAuthenticationConverter(); // 自定义增强用户认证转换器
accessTokenConverter.setUserTokenConverter(userTokenConverter);
// 增强用户认证转换器
remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
// 使用远程令牌服务
resources.tokenServices(remoteTokenServices);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().csrf().disable();
}
}
// 关于资源服务器检验token
// 对所有需要令牌的资源生效
OAuth2AuthenticationProcessingFilter
// 使用BearerTokenExtractor在请求头中提取token
BearerTokenExtractor.extract()
// bearer token 生成预认证令牌 PreAuthenticatedAuthenticationToken
// 使用OAuth2AuthenticationManager认证
OAuth2AuthenticationManager.authenticate()
// 使用远程token服务加载令牌 默认使用DefaultTokenServices 可以通过
// ResourceServerSecurityConfigurer.tokenServices 配置自定义资源服务器
ResourceServerConfigurer.configure
// 加载令牌
RemoteTokenServices.loadAuthentication()
// 默认访问令牌转换器
DefaultAccessTokenConverter.extractAuthentication()
// 默认用户认证转换器
DefaultUserAuthenticationConverter.extractAuthentication()
// 至此 用户认证信息 已经成功提取到
// 关于资源服务器是如何配置 OAuth2AuthenticationProcessingFilter 到容器的
@EnableResourceServer
// 注入ResourceServerConfiguration到容器
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered{
protected void configure(HttpSecurity http) -> {
ResourceServerTokenServices services = this.resolveTokenServices();
// 配置资源服务安全配置
ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer(){
public void configure(HttpSecurity http){
// 配置 O Auth 2身份验证处理过滤器
AuthenticationManager oauthAuthenticationManager = this.oauthAuthenticationManager(http);
this.resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
this.resourcesServerFilter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
this.resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
}
};
http.apply(resources);
}
}
// 至此过滤器配置完成