spring cloud oauth2 使用说明
************************
导入 jar 包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
说明:spring-cloud-starter-oauth2中已经包含了security,不需要再导入security
************************
自动配置类
OAuth2AutoConfiguration:oauth2自动配置类
@Configuration
@ConditionalOnClass({OAuth2AccessToken.class, WebMvcConfigurer.class})
@Import({OAuth2AuthorizationServerConfiguration.class, OAuth2MethodSecurityConfiguration.class, OAuth2ResourceServerConfiguration.class, OAuth2RestOperationsConfiguration.class})
@AutoConfigureBefore({WebMvcAutoConfiguration.class})
@EnableConfigurationProperties({OAuth2ClientProperties.class})
public class OAuth2AutoConfiguration {
private final OAuth2ClientProperties credentials;
public OAuth2AutoConfiguration(OAuth2ClientProperties credentials) {
this.credentials = credentials;
}
@Bean
public ResourceServerProperties resourceServerProperties() {
return new ResourceServerProperties(this.credentials.getClientId(), this.credentials.getClientSecret());
}
}
说明:该自动配置类导入OAuth2AuthorizationServerConfiguration、OAuth2MethodSecurityConfiguration、OAuth2ResourceServerConfiguration、OAuth2RestOperationsConfiguration,
读取属性文件配置OAuth2ClientProperties
OAuth2ClientProperties
@ConfigurationProperties(
prefix = "security.oauth2.client"
)
public class OAuth2ClientProperties {
private String clientId;
private String clientSecret = UUID.randomUUID().toString();
private boolean defaultSecret = true;
public OAuth2ClientProperties() {
OAuth2AuthorizationServerConfiguration:oauth2认证服务器配置
@Configuration
@ConditionalOnClass({EnableAuthorizationServer.class})
@ConditionalOnMissingBean({AuthorizationServerConfigurer.class})
//当容器中不存在authorizationServerConfigurer实例时创建OAuth2AuthorizationServerConfiguration,
一般情况下,资源服务器会创建authorizationServerConfigurer的实例
@ConditionalOnBean({AuthorizationServerEndpointsConfiguration.class})
@EnableConfigurationProperties({AuthorizationServerProperties.class})
@Import({AuthorizationServerTokenServicesConfiguration.class})
public class OAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final Log logger = LogFactory.getLog(OAuth2AuthorizationServerConfiguration.class);
private final BaseClientDetails details;
private final AuthenticationManager authenticationManager;
private final TokenStore tokenStore;
private final AccessTokenConverter tokenConverter;
private final AuthorizationServerProperties properties;
****************
内部类
@Configuration
@ConditionalOnMissingBean({BaseClientDetails.class})
protected static class BaseClientDetailsConfiguration {
private final OAuth2ClientProperties client;
protected BaseClientDetailsConfiguration(OAuth2ClientProperties client) {
this.client = client;
}
@Bean
@ConfigurationProperties(
prefix = "security.oauth2.client"
)
public BaseClientDetails oauth2ClientDetails() {
BaseClientDetails details = new BaseClientDetails();
if (this.client.getClientId() == null) {
this.client.setClientId(UUID.randomUUID().toString());
}
details.setClientId(this.client.getClientId());
details.setClientSecret(this.client.getClientSecret());
details.setAuthorizedGrantTypes(Arrays.asList("authorization_code", "password", "client_credentials", "implicit", "refresh_token"));
details.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
details.setRegisteredRedirectUri(Collections.emptySet());
return details;
}
}
BaseClientDetails:客户端配置属性
public class BaseClientDetails implements ClientDetails {
@JsonProperty("client_id")
@com.fasterxml.jackson.annotation.JsonProperty("client_id")
private String clientId;
@JsonProperty("client_secret")
@com.fasterxml.jackson.annotation.JsonProperty("client_secret")
private String clientSecret;
@JsonDeserialize(using = JacksonArrayOrStringDeserializer.class)
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class)
private Set<String> scope;
@JsonProperty("resource_ids")
@JsonDeserialize(using = JacksonArrayOrStringDeserializer.class)
@com.fasterxml.jackson.annotation.JsonProperty("resource_ids")
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class)
private Set<String> resourceIds;
@JsonProperty("authorized_grant_types")
@JsonDeserialize(using = JacksonArrayOrStringDeserializer.class)
@com.fasterxml.jackson.annotation.JsonProperty("authorized_grant_types")
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class)
private Set<String> authorizedGrantTypes;
@JsonProperty("redirect_uri")
@JsonDeserialize(using = JacksonArrayOrStringDeserializer.class)
@com.fasterxml.jackson.annotation.JsonProperty("redirect_uri")
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class)
private Set<String> registeredRedirectUris;
@JsonProperty("autoapprove")
@JsonDeserialize(using = JacksonArrayOrStringDeserializer.class)
@com.fasterxml.jackson.annotation.JsonProperty("autoapprove")
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = Jackson2ArrayOrStringDeserializer.class)
private Set<String> autoApproveScopes;
private List<GrantedAuthority> authorities;
@JsonProperty("access_token_validity")
@com.fasterxml.jackson.annotation.JsonProperty("access_token_validity")
private Integer accessTokenValiditySeconds;
@JsonProperty("refresh_token_validity")
@com.fasterxml.jackson.annotation.JsonProperty("refresh_token_validity")
private Integer refreshTokenValiditySeconds;
@JsonIgnore
@com.fasterxml.jackson.annotation.JsonIgnore
private Map<String, Object> additionalInformation;
AuthorizationServerProperties:认证服务器自动配置属性
@ConfigurationProperties(
prefix = "security.oauth2.authorization"
)
public class AuthorizationServerProperties {
private String checkTokenAccess;
private String tokenKeyAccess;
private String realm;
private AuthorizationServerProperties.Jwt jwt = new AuthorizationServerProperties.Jwt();
public AuthorizationServerProperties() {
***************
内部类
public class Jwt {
private String keyValue;
private String keyStore;
private String keyStorePassword;
private String keyAlias;
private String keyPassword;
public Jwt() {
ResourceServerProperties:资源服务器自动配置属性
@ConfigurationProperties(
prefix = "security.oauth2.resource"
)
public class ResourceServerProperties implements BeanFactoryAware, InitializingBean {
@JsonIgnore
private final String clientId;
@JsonIgnore
private final String clientSecret;
@JsonIgnore
private ListableBeanFactory beanFactory;
private String serviceId;
private String id;
private String userInfoUri;
private String tokenInfoUri;
private boolean preferTokenInfo;
private String tokenType;
private ResourceServerProperties.Jwt jwt;
private ResourceServerProperties.Jwk jwk;
public ResourceServerProperties() {
this((String)null, (String)null);
}
public ResourceServerProperties(String clientId, String clientSecret) {
***************
内部类
public class Jwk {
private String keySetUri;
public Jwk() {
***************
内部类
public class Jwt {
private String keyValue;
private String keyUri;
private String keyStore;
private String keyStorePassword;
private String keyAlias;
private String keyPassword;
public Jwt() {
************************
认证服务器配置
*********************
相关注解
@EnableAuthorizationServer:启动认证服务器
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {
}
说明:注解导入AuthorizationServerEndpointsConfiguration、AuthorizationServerSecurityConfiguration
*********************
注解导入类
AuthorizationServerEndpointsConfiguration:认证服务器endPoints配置
@Configuration
@Import({AuthorizationServerEndpointsConfiguration.TokenKeyEndpointRegistrar.class})
public class AuthorizationServerEndpointsConfiguration {
private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
public AuthorizationServerEndpointsConfiguration() {
}
@PostConstruct
public void init() {
Iterator var1 = this.configurers.iterator();
while(var1.hasNext()) {
AuthorizationServerConfigurer configurer = (AuthorizationServerConfigurer)var1.next();
try {
configurer.configure(this.endpoints);
} catch (Exception var4) {
throw new IllegalStateException("Cannot configure enpdoints", var4);
}
}
this.endpoints.setClientDetailsService(this.clientDetailsService);
}
@Bean
public AuthorizationEndpoint authorizationEndpoint() throws Exception {
AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();
FrameworkEndpointHandlerMapping mapping = this.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
authorizationEndpoint.setUserApprovalPage(this.extractPath(mapping, "/oauth/confirm_access"));
authorizationEndpoint.setProviderExceptionHandler(this.exceptionTranslator());
authorizationEndpoint.setErrorPage(this.extractPath(mapping, "/oauth/error"));
authorizationEndpoint.setTokenGranter(this.tokenGranter());
authorizationEndpoint.setClientDetailsService(this.clientDetailsService);
authorizationEndpoint.setAuthorizationCodeServices(this.authorizationCodeServices());
authorizationEndpoint.setOAuth2RequestFactory(this.oauth2RequestFactory());
authorizationEndpoint.setOAuth2RequestValidator(this.oauth2RequestValidator());
authorizationEndpoint.setUserApprovalHandler(this.userApprovalHandler());
authorizationEndpoint.setRedirectResolver(this.redirectResolver());
return authorizationEndpoint;
}
@Bean
public TokenEndpoint tokenEndpoint() throws Exception {
TokenEndpoint tokenEndpoint = new TokenEndpoint();
tokenEndpoint.setClientDetailsService(this.clientDetailsService);
tokenEndpoint.setProviderExceptionHandler(this.exceptionTranslator());
tokenEndpoint.setTokenGranter(this.tokenGranter());
tokenEndpoint.setOAuth2RequestFactory(this.oauth2RequestFactory());
tokenEndpoint.setOAuth2RequestValidator(this.oauth2RequestValidator());
tokenEndpoint.setAllowedRequestMethods(this.allowedTokenEndpointRequestMethods());
return tokenEndpoint;
}
@Bean
public CheckTokenEndpoint checkTokenEndpoint() {
CheckTokenEndpoint endpoint = new CheckTokenEndpoint(this.getEndpointsConfigurer().getResourceServerTokenServices());
endpoint.setAccessTokenConverter(this.getEndpointsConfigurer().getAccessTokenConverter());
endpoint.setExceptionTranslator(this.exceptionTranslator());
return endpoint;
}
@Bean
public WhitelabelApprovalEndpoint whitelabelApprovalEndpoint() {
return new WhitelabelApprovalEndpoint();
}
@Bean
public WhitelabelErrorEndpoint whitelabelErrorEndpoint() {
return new WhitelabelErrorEndpoint();
}
@Bean
public FrameworkEndpointHandlerMapping oauth2EndpointHandlerMapping() throws Exception {
return this.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping();
}
@Bean
public FactoryBean<ConsumerTokenServices> consumerTokenServices() throws Exception {
return new AbstractFactoryBean<ConsumerTokenServices>() {
public Class<?> getObjectType() {
return ConsumerTokenServices.class;
}
protected ConsumerTokenServices createInstance() throws Exception {
return AuthorizationServerEndpointsConfiguration.this.getEndpointsConfigurer().getConsumerTokenServices();
}
};
}
@Bean
public FactoryBean<AuthorizationServerTokenServices> defaultAuthorizationServerTokenServices() {
return new AuthorizationServerEndpointsConfiguration.AuthorizationServerTokenServicesFactoryBean(this.endpoints);
}
@Component
protected static class TokenKeyEndpointRegistrar implements BeanDefinitionRegistryPostProcessor {
private BeanDefinitionRegistry registry;
protected TokenKeyEndpointRegistrar() {
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, JwtAccessTokenConverter.class, false, false);
if (names.length > 0) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
builder.addConstructorArgReference(names[0]);
this.registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
}
}//注册tokenKeyPoint类
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.registry = registry;
}
}
protected static class AuthorizationServerTokenServicesFactoryBean extends AbstractFactoryBean<AuthorizationServerTokenServices> {
private AuthorizationServerEndpointsConfigurer endpoints;
protected AuthorizationServerTokenServicesFactoryBean() {
}
public AuthorizationServerTokenServicesFactoryBean(AuthorizationServerEndpointsConfigurer endpoints) {
this.endpoints = endpoints;
}
public Class<?> getObjectType() {
return AuthorizationServerTokenServices.class;
}
protected AuthorizationServerTokenServices createInstance() throws Exception {
return this.endpoints.getDefaultAuthorizationServerTokenServices();
}
}
}
AuthorizationServerEndpointsConfigurer:认证服务器endpoints配置参数
public final class AuthorizationServerEndpointsConfigurer {
private AuthorizationServerTokenServices tokenServices;
private ConsumerTokenServices consumerTokenServices;
private AuthorizationCodeServices authorizationCodeServices;
private ResourceServerTokenServices resourceTokenServices;
private TokenStore tokenStore; //设置token存储方式
private TokenEnhancer tokenEnhancer; //jwt添加自定义字段
private AccessTokenConverter accessTokenConverter; //jwt数据转换设置key
private ApprovalStore approvalStore;
private TokenGranter tokenGranter;
private OAuth2RequestFactory requestFactory;
private OAuth2RequestValidator requestValidator;
private UserApprovalHandler userApprovalHandler;
private AuthenticationManager authenticationManager; //password认证时使用
private ClientDetailsService clientDetailsService;
private String prefix;
private Map<String, String> patternMap = new HashMap();
private Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet();
private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
private boolean approvalStoreDisabled;
private List<Object> interceptors = new ArrayList();
private DefaultTokenServices defaultTokenServices;
private UserDetailsService userDetailsService; //认证服务器用户认证
private boolean tokenServicesOverride = false;
private boolean userDetailsServiceOverride = false;
private boolean reuseRefreshToken = true;
private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator;
private RedirectResolver redirectResolver;
*****************
常用方法
public AuthorizationServerEndpointsConfigurer authenticationManager(AuthenticationManager authenticationManager) {
public AuthorizationServerEndpointsConfigurer userDetailsService(UserDetailsService userDetailsService) {
public AuthorizationServerEndpointsConfigurer tokenStore(TokenStore tokenStore) {
public AuthorizationServerEndpointsConfigurer tokenEnhancer(TokenEnhancer tokenEnhancer) {
public AuthorizationServerEndpointsConfigurer accessTokenConverter(AccessTokenConverter accessTokenConverter) {
支持的token存储方式
JwkTokenStore
public class JwtTokenStore implements TokenStore {
private JwtAccessTokenConverter jwtTokenEnhancer;
private ApprovalStore approvalStore;
********************
JwtTokenStore
public class JwtTokenStore implements TokenStore {
private JwtAccessTokenConverter jwtTokenEnhancer;
private ApprovalStore approvalStore;
public JwtTokenStore(JwtAccessTokenConverter jwtTokenEnhancer) {
********************
RedisTokenStore
public class RedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
private static final String UNAME_TO_ACCESS = "uname_to_access:";
private static final boolean springDataRedis_2_0 = ClassUtils.isPresent("org.springframework.data.redis.connection.RedisStandaloneConfiguration", RedisTokenStore.class.getClassLoader());
private final RedisConnectionFactory connectionFactory;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private String prefix = "";
private Method redisConnectionSet_2_0;
public RedisTokenStore(RedisConnectionFactory connectionFactory) {
********************
InMemoryTokenStore
public class InMemoryTokenStore implements TokenStore {
private static final int DEFAULT_FLUSH_INTERVAL = 1000;
private final ConcurrentHashMap<String, OAuth2AccessToken> accessTokenStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, OAuth2AccessToken> authenticationToAccessTokenStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> userNameToAccessTokenStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> clientIdToAccessTokenStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, OAuth2RefreshToken> refreshTokenStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, String> accessTokenToRefreshTokenStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, OAuth2Authentication> authenticationStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, OAuth2Authentication> refreshTokenAuthenticationStore = new ConcurrentHashMap();
private final ConcurrentHashMap<String, String> refreshTokenToAccessTokenStore = new ConcurrentHashMap();
private final DelayQueue<InMemoryTokenStore.TokenExpiry> expiryQueue = new DelayQueue();
private final ConcurrentHashMap<String, InMemoryTokenStore.TokenExpiry> expiryMap = new ConcurrentHashMap();
private int flushInterval = 1000;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private AtomicInteger flushCounter = new AtomicInteger(0);
public InMemoryTokenStore() {
********************
JdbcTokenStore
public class JdbcTokenStore implements TokenStore {
private static final Log LOG = LogFactory.getLog(JdbcTokenStore.class);
private static final String DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT = "insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)";
private static final String DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_access_token where token_id = ?";
private static final String DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_access_token where token_id = ?";
private static final String DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT = "select token_id, token from oauth_access_token where authentication_id = ?";
private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ? and client_id = ?";
private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ?";
private static final String DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT = "select token_id, token from oauth_access_token where client_id = ?";
private static final String DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT = "delete from oauth_access_token where token_id = ?";
private static final String DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT = "delete from oauth_access_token where refresh_token = ?";
private static final String DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT = "insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)";
private static final String DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_refresh_token where token_id = ?";
private static final String DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_refresh_token where token_id = ?";
private static final String DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT = "delete from oauth_refresh_token where token_id = ?";
private String insertAccessTokenSql = "insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)";
private String selectAccessTokenSql = "select token_id, token from oauth_access_token where token_id = ?";
private String selectAccessTokenAuthenticationSql = "select token_id, authentication from oauth_access_token where token_id = ?";
private String selectAccessTokenFromAuthenticationSql = "select token_id, token from oauth_access_token where authentication_id = ?";
private String selectAccessTokensFromUserNameAndClientIdSql = "select token_id, token from oauth_access_token where user_name = ? and client_id = ?";
private String selectAccessTokensFromUserNameSql = "select token_id, token from oauth_access_token where user_name = ?";
private String selectAccessTokensFromClientIdSql = "select token_id, token from oauth_access_token where client_id = ?";
private String deleteAccessTokenSql = "delete from oauth_access_token where token_id = ?";
private String insertRefreshTokenSql = "insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)";
private String selectRefreshTokenSql = "select token_id, token from oauth_refresh_token where token_id = ?";
private String selectRefreshTokenAuthenticationSql = "select token_id, authentication from oauth_refresh_token where token_id = ?";
private String deleteRefreshTokenSql = "delete from oauth_refresh_token where token_id = ?";
private String deleteAccessTokenFromRefreshTokenSql = "delete from oauth_access_token where refresh_token = ?";
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private final JdbcTemplate jdbcTemplate;
public JdbcTokenStore(DataSource dataSource) {
AuthorizationServerSecurityConfiguration:认证服务器安全配置
@Configuration
@Order(0)
@Import({ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class})
//该配置类导入ClientDetailsServiceConfiguration、AuthorizationServerEndpointsConfiguration
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
public AuthorizationServerSecurityConfiguration() {
}
@Autowired
public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
Iterator var2 = this.configurers.iterator();
while(var2.hasNext()) {
AuthorizationServerConfigurer configurer = (AuthorizationServerConfigurer)var2.next();
configurer.configure(clientDetails);
}
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
FrameworkEndpointHandlerMapping handlerMapping = this.endpoints.oauth2EndpointHandlerMapping();
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
this.configure(configurer);
http.apply(configurer);
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
if (!this.endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
UserDetailsService userDetailsService = (UserDetailsService)http.getSharedObject(UserDetailsService.class);
this.endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
}
((RequestMatcherConfigurer)((HttpSecurity)((AuthorizedUrl)((AuthorizedUrl)((AuthorizedUrl)http.authorizeRequests().antMatchers(new String[]{tokenEndpointPath})).fullyAuthenticated().antMatchers(new String[]{tokenKeyPath})).access(configurer.getTokenKeyAccess()).antMatchers(new String[]{checkTokenPath})).access(configurer.getCheckTokenAccess()).and()).requestMatchers().antMatchers(new String[]{tokenEndpointPath, tokenKeyPath, checkTokenPath})).and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.setSharedObject(ClientDetailsService.class, this.clientDetailsService);
}
protected void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
Iterator var2 = this.configurers.iterator();
while(var2.hasNext()) {
AuthorizationServerConfigurer configurer = (AuthorizationServerConfigurer)var2.next();
configurer.configure(oauthServer);
}
}
}
ClientDetailsServiceConfiguration:认证客户端配置
@Configuration
public class ClientDetailsServiceConfiguration {
private ClientDetailsServiceConfigurer configurer = new ClientDetailsServiceConfigurer(new ClientDetailsServiceBuilder());
public ClientDetailsServiceConfiguration() {
}
@Bean
public ClientDetailsServiceConfigurer clientDetailsServiceConfigurer() {
return this.configurer;
}
@Bean
@Lazy
@Scope(
proxyMode = ScopedProxyMode.INTERFACES
)
public ClientDetailsService clientDetailsService() throws Exception {
return ((ClientDetailsServiceBuilder)this.configurer.and()).build();
}
}
ClientDetailsServiceConfigurer:认证客户端配置
public class ClientDetailsServiceConfigurer extends SecurityConfigurerAdapter<ClientDetailsService, ClientDetailsServiceBuilder<?>> {
public ClientDetailsServiceConfigurer(ClientDetailsServiceBuilder<?> builder) {
this.setBuilder(builder);
}
public ClientDetailsServiceBuilder<?> withClientDetails(ClientDetailsService clientDetailsService) throws Exception {
this.setBuilder(((ClientDetailsServiceBuilder)this.getBuilder()).clients(clientDetailsService));
return (ClientDetailsServiceBuilder)this.and();
}
public InMemoryClientDetailsServiceBuilder inMemory() throws Exception {
InMemoryClientDetailsServiceBuilder next = ((ClientDetailsServiceBuilder)this.getBuilder()).inMemory();
this.setBuilder(next);
return next;
}
public JdbcClientDetailsServiceBuilder jdbc(DataSource dataSource) throws Exception {
JdbcClientDetailsServiceBuilder next = ((ClientDetailsServiceBuilder)this.getBuilder()).jdbc().dataSource(dataSource);
this.setBuilder(next);
return next;
}
public void init(ClientDetailsServiceBuilder<?> builder) throws Exception {
}
public void configure(ClientDetailsServiceBuilder<?> builder) throws Exception {
}
}
ClientDetailsServiceBuilder:构造clientDetailsService
public class ClientDetailsServiceBuilder<B extends ClientDetailsServiceBuilder<B>> extends SecurityConfigurerAdapter<ClientDetailsService, B> implements SecurityBuilder<ClientDetailsService> {
private List<ClientDetailsServiceBuilder<B>.ClientBuilder> clientBuilders = new ArrayList();
public ClientDetailsServiceBuilder() {
}
public InMemoryClientDetailsServiceBuilder inMemory() throws Exception {
return new InMemoryClientDetailsServiceBuilder();
}
public JdbcClientDetailsServiceBuilder jdbc() throws Exception {
return new JdbcClientDetailsServiceBuilder();
}
public ClientDetailsServiceBuilder<?> clients(final ClientDetailsService clientDetailsService) throws Exception {
public ClientDetailsServiceBuilder<B>.ClientBuilder withClient(String clientId) {
public ClientDetailsService build() throws Exception {
********************
内部类
public final class ClientBuilder {
private final String clientId;
private Collection<String> authorizedGrantTypes;
private Collection<String> authorities;
private Integer accessTokenValiditySeconds;
private Integer refreshTokenValiditySeconds;
private Collection<String> scopes;
private Collection<String> autoApproveScopes;
private String secret;
private Set<String> registeredRedirectUris;
private Set<String> resourceIds;
private boolean autoApprove;
private Map<String, Object> additionalInformation;
public ClientDetailsServiceBuilder<B>.ClientBuilder resourceIds(String... resourceIds) {
public ClientDetailsServiceBuilder<B>.ClientBuilder redirectUris(String... registeredRedirectUris) {
public ClientDetailsServiceBuilder<B>.ClientBuilder authorizedGrantTypes(String... authorizedGrantTypes) {
public ClientDetailsServiceBuilder<B>.ClientBuilder accessTokenValiditySeconds(int accessTokenValiditySeconds) {
public ClientDetailsServiceBuilder<B>.ClientBuilder refreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
public ClientDetailsServiceBuilder<B>.ClientBuilder secret(String secret) {
public ClientDetailsServiceBuilder<B>.ClientBuilder scopes(String... scopes) {
public ClientDetailsServiceBuilder<B>.ClientBuilder authorities(String... authorities) {
public ClientDetailsServiceBuilder<B>.ClientBuilder autoApprove(boolean autoApprove) {
public ClientDetailsServiceBuilder<B>.ClientBuilder autoApprove(String... scopes) {
public ClientDetailsServiceBuilder<B>.ClientBuilder additionalInformation(Map<String, ?> map) {
public ClientDetailsServiceBuilder<B>.ClientBuilder additionalInformation(String... pairs) {
String[] var2 = pairs;
int var3 = pairs.length;
for(int var4 = 0; var4 < var3; ++var4) {
String pair = var2[var4];
String separator = ":"; //additionalInformation的字符串使用“:”做分隔符
if (!pair.contains(separator) && pair.contains("=")) {
separator = "=";
}
int index = pair.indexOf(separator);
String key = pair.substring(0, index > 0 ? index : pair.length());
String value = index > 0 ? pair.substring(index + 1) : null;
this.additionalInformation.put(key, value);
}
return this;
}
public ClientDetailsServiceBuilder<B> and() {
private ClientBuilder(String clientId) {
private ClientDetails build() {
********************
自定义认证服务器
AuthorizationServerConfigurer:认证服务器接口
public interface AuthorizationServerConfigurer {
void configure(AuthorizationServerSecurityConfigurer var1) throws Exception;
void configure(ClientDetailsServiceConfigurer var1) throws Exception;
void configure(AuthorizationServerEndpointsConfigurer var1) throws Exception;
}
AuthorizationServerConfigurerAdapter:认证服务器类
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
public AuthorizationServerConfigurerAdapter() {
}
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
AuthorizationServerSecurityConfigurer:认证服务器安全配置
public final class AuthorizationServerSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private AuthenticationEntryPoint authenticationEntryPoint;
private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
private PasswordEncoder passwordEncoder;
private String realm = "oauth2/client";
private boolean allowFormAuthenticationForClients = false;
private String tokenKeyAccess = "denyAll()";
private String checkTokenAccess = "denyAll()"; //使用spring security表达式,默认值为denyAll():拒绝所有请求
permitAll():允许所有请求
isAnonymous():是否匿名登陆
isAuthenticated():不是匿名认证时为true
isRemberMe():使用remember me认证时为true
isFullyAuthenticated():是否是全认证,不包含remember me认证
private boolean sslOnly = false;
private List<Filter> tokenEndpointAuthenticationFilters = new ArrayList();
public AuthorizationServerSecurityConfigurer() {
}
****************
普通方法
public AuthorizationServerSecurityConfigurer allowFormAuthenticationForClients() {
public AuthorizationServerSecurityConfigurer tokenKeyAccess(String tokenKeyAccess) {
public AuthorizationServerSecurityConfigurer checkTokenAccess(String checkTokenAccess) {
public AuthorizationServerSecurityConfigurer passwordEncoder(PasswordEncoder passwordEncoder) {
public AuthorizationServerSecurityConfigurer sslOnly() {
public AuthorizationServerSecurityConfigurer realm(String realm) {
public AuthorizationServerSecurityConfigurer authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
public AuthorizationServerSecurityConfigurer accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
public void addTokenEndpointAuthenticationFilter(Filter filter) {
public void tokenEndpointAuthenticationFilters(List<Filter> filters) {
spring security表达式
public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
protected final Authentication authentication;
private AuthenticationTrustResolver trustResolver;
private RoleHierarchy roleHierarchy;
private Set<String> roles;
private String defaultRolePrefix = "ROLE_";
public final boolean permitAll = true;
public final boolean denyAll = false;
private PermissionEvaluator permissionEvaluator;
public final String read = "read";
public final String write = "write";
public final String create = "create";
public final String delete = "delete";
public final String admin = "administration";
**************
普通方法
public final boolean hasAuthority(String authority) {
public final boolean hasAnyAuthority(String... authorities) {
public final boolean hasRole(String role) {
public final boolean hasAnyRole(String... roles) {
public final boolean permitAll() {
public final boolean denyAll() {
public final boolean isAnonymous() {
public final boolean isAuthenticated() {
public final boolean isRememberMe() {
public final boolean isFullyAuthenticated() {
************************
资源服务器配置
*********************
相关注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({ResourceServerConfiguration.class})
public @interface EnableResourceServer {
}
*********************
注解导入类
ResourceServerConfiguration:资源服务器配置
@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() {
*********************
自定义资源服务器
ResourceServerConfigurer
public interface ResourceServerConfigurer {
void configure(ResourceServerSecurityConfigurer var1) throws Exception;
void configure(HttpSecurity var1) throws Exception;
}
ResourceServerConfigurerAdapter
public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
public ResourceServerConfigurerAdapter() {
}
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
}
public void configure(HttpSecurity http) throws Exception {
((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated();
}
}
ResourceServerSecurityConfigurer:资源服务器安全配置
public final class ResourceServerSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
private OAuth2AuthenticationProcessingFilter resourcesServerFilter;
private AuthenticationManager authenticationManager;
private AuthenticationEventPublisher eventPublisher = null;
private ResourceServerTokenServices resourceTokenServices; //设置token编码、解码
private TokenStore tokenStore = new InMemoryTokenStore(); //token存储,默认存储在内存中
private String resourceId = "oauth2-resource";
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new OAuth2WebSecurityExpressionHandler();
private TokenExtractor tokenExtractor;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private boolean stateless = true;
public ResourceServerSecurityConfigurer() {
this.resourceId(this.resourceId);
}
*****************
普通方法
public ResourceServerSecurityConfigurer authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
public ResourceServerSecurityConfigurer accessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
//自定义权限不足时的相关处理
public ResourceServerSecurityConfigurer tokenStore(TokenStore tokenStore) {
//设置token存储方式
public ResourceServerSecurityConfigurer resourceId(String resourceId) {
//设置resourceId
public void configure(HttpSecurity http) throws Exception {
public ResourceServerSecurityConfigurer eventPublisher(AuthenticationEventPublisher eventPublisher) {
public ResourceServerSecurityConfigurer expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
public ResourceServerSecurityConfigurer tokenExtractor(TokenExtractor tokenExtractor) {
public ResourceServerSecurityConfigurer authenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
public ResourceServerSecurityConfigurer authenticationManager(AuthenticationManager authenticationManager) {
public ResourceServerSecurityConfigurer tokenServices(ResourceServerTokenServices tokenServices) {
ResourceServerTokenServices
public interface ResourceServerTokenServices {
OAuth2Authentication loadAuthentication(String var1) throws AuthenticationException, InvalidTokenException;
OAuth2AccessToken readAccessToken(String var1);
}
RemoteTokenServices
public class RemoteTokenServices implements ResourceServerTokenServices {
protected final Log logger = LogFactory.getLog(this.getClass());
private RestOperations restTemplate = new RestTemplate();
private String checkTokenEndpointUrl;
private String clientId;
private String clientSecret;
private String tokenName = "token";
private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
public RemoteTokenServices() {
public void setRestTemplate(RestOperations restTemplate) {
public void setCheckTokenEndpointUrl(String checkTokenEndpointUrl) {
public void setClientId(String clientId) {
public void setClientSecret(String clientSecret) {
public void setAccessTokenConverter(AccessTokenConverter accessTokenConverter) {
public void setTokenName(String tokenName) {
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
public OAuth2AccessToken readAccessToken(String accessToken) {
************************
客户端配置
*********************
相关注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({OAuth2ClientConfiguration.class})
public @interface EnableOAuth2Client {
}
*********************
注解导入类
OAuth2ClientConfiguration:oauth2客户端配置
@Configuration
public class OAuth2ClientConfiguration {
public OAuth2ClientConfiguration() {
}
@Bean
public OAuth2ClientContextFilter oauth2ClientContextFilter() {
OAuth2ClientContextFilter filter = new OAuth2ClientContextFilter();
return filter;
}//存储当前请求和上下文请求
@Bean
@Scope(
value = "request",
proxyMode = ScopedProxyMode.INTERFACES
)
protected AccessTokenRequest accessTokenRequest(@Value("#{request.parameterMap}") Map<String, String[]> parameters, @Value("#{request.getAttribute('currentUri')}") String currentUri) {
DefaultAccessTokenRequest request = new DefaultAccessTokenRequest(parameters);
request.setCurrentUri(currentUri);
return request;
}//获取accessToken的请求
@Configuration
protected static class OAuth2ClientContextConfiguration {
@Resource
@Qualifier("accessTokenRequest")
private AccessTokenRequest accessTokenRequest;
protected OAuth2ClientContextConfiguration() {
}
@Bean
@Scope(
value = "session",
proxyMode = ScopedProxyMode.INTERFACES
)
public OAuth2ClientContext oauth2ClientContext() {
return new DefaultOAuth2ClientContext(this.accessTokenRequest);
}//session范围内存储token
}
}
*********************
feign使用oauth2注入bean
ClientCredentialsResourceDetails:设置客户端信息
public class ClientCredentialsResourceDetails extends BaseOAuth2ProtectedResourceDetails {
public ClientCredentialsResourceDetails() {
this.setGrantType("client_credentials");
}
public boolean isClientOnly() {
return true;
}
}
BaseOAuth2ProtectedResourceDetails
public class BaseOAuth2ProtectedResourceDetails implements OAuth2ProtectedResourceDetails {
private String id;
private String grantType = "unsupported";
private String clientId;
private String accessTokenUri;
private List<String> scope;
private String clientSecret;
private AuthenticationScheme clientAuthenticationScheme;
private AuthenticationScheme authorizationScheme;
private String tokenName;
public BaseOAuth2ProtectedResourceDetails() {
OAuth2FeignRequestInterceptor:feign调用请求拦截器
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
public static final String BEARER = "Bearer";
public static final String AUTHORIZATION = "Authorization";
private final OAuth2ClientContext oAuth2ClientContext;
private final OAuth2ProtectedResourceDetails resource;
private final String tokenType;
private final String header;
private AccessTokenProvider accessTokenProvider;
***************
构造方法
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource) {
public OAuth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource, String tokenType, String header) {
***************
普通方法
public void apply(RequestTemplate template) {
protected String extract(String tokenType) {
public OAuth2AccessToken getToken() {
protected OAuth2AccessToken acquireAccessToken() throws UserRedirectRequiredException {
public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) {
OAuth2RestTemplate:oauth2客户端请求操作类
public class OAuth2RestTemplate extends RestTemplate implements OAuth2RestOperations {
private final OAuth2ProtectedResourceDetails resource;
private AccessTokenProvider accessTokenProvider;
private OAuth2ClientContext context;
private boolean retryBadAccessTokens;
private OAuth2RequestAuthenticator authenticator;
***************
构造方法
public OAuth2RestTemplate(OAuth2ProtectedResourceDetails resource) {
public OAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
***************
普通方法
public void setAuthenticator(OAuth2RequestAuthenticator authenticator) {
public void setRetryBadAccessTokens(boolean retryBadAccessTokens) {
public void setErrorHandler(ResponseErrorHandler errorHandler) {
public OAuth2ProtectedResourceDetails getResource() {
public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException {
public OAuth2ClientContext getOAuth2ClientContext() {
public void setAccessTokenProvider(AccessTokenProvider accessTokenProvider) {