spring-security-oauth2支持的註解有:web
1.EnableOAuth2Clientspring
適用於使用spring security,而且想從Oauth2認證服務器來獲取受權的web應用環境代碼中,它啓用了一個Oauth2 客戶端配置。爲了更好的利用這個特性,須要在客戶端應用中的DelegatingFilterProxy(代理一個名爲oauth2ClientContextFilter)增長一個servlet filter。當filter配置到client app時,可使用註解@AccessTokenRequest提供的另外一個bean來建立一個Oauth2RequestTemplate。示例:express
@Configuration @EnableOAuth2Client public class RemoteResourceConfiguration { @Bean public OAuth2RestOperations restTemplate(OAuth2ClientContext oauth2ClientContext) { return new OAuth2RestTemplate(remote(), oauth2ClientContext); } }
Client App使用client credential受權,不須要AccessTokenRequest或者域內RestOperation(對app來講,狀態是全局的),但在須要時仍然使用filter來觸發OAuth2RestOperation來獲取token。使用密碼受權的app須要在RestOperation動做以前爲OAuth2ProtectedResouceDetail設置認證屬性,這就是說,resouce detail 自己也須要session(假設系統中有多個用戶)。安全
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(OAuth2ClientConfiguration.class) public @interface EnableOAuth2Client { }
實現OAuth2ClientConfiguration服務器
@Configuration public class 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; } @Configuration protected static class OAuth2ClientContextConfiguration { @Resource @Qualifier("accessTokenRequest") private AccessTokenRequest accessTokenRequest; @Bean @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) public OAuth2ClientContext oauth2ClientContext() { return new DefaultOAuth2ClientContext(accessTokenRequest); } } }
2. EnableAuthorizationServersession
工具方法,用來在當前應用context裏(必須是一個DispatcherServlet context)開啓一個受權server(例如AuthorizationEndpoint)和一個TokenEndpoint。server的多個屬性能夠經過自定義AuthorizationServerConfigurer類型(如AuthorizationServerConfigurerAdapter的擴展)的Bean來定製。經過正常使用spring security的特點EnableWebSecurity,用戶負責保證受權Endpoint(/oauth/authorize)的安全,但Token Endpoint(/oauth/token)將自動使用http basic的客戶端憑證來保證安全。經過一個或者多個AuthorizationServerConfigurer提供一個ClientDetailService來註冊client(必須)。app
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class}) public @interface EnableAuthorizationServer { }
2.1 AuthorizationServerEndpointsConfigurationless
private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer(); @Autowired private ClientDetailsService clientDetailsService; @Autowired private List<AuthorizationServerConfigurer> configurers = Collections.emptyList(); @PostConstruct public void init() { for (AuthorizationServerConfigurer configurer : configurers) { try { configurer.configure(endpoints); } catch (Exception e) { throw new IllegalStateException("Cannot configure enpdoints", e); } } endpoints.setClientDetailsService(clientDetailsService); }
@Component protected static class TokenKeyEndpointRegistrar implements BeanDefinitionRegistryPostProcessor { private BeanDefinitionRegistry registry; @Override 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]); registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition()); } } @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { this.registry = registry; } }
2.2 AuthorizationServerSecurityConfigurationide
@Configuration @Order(0) @Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class }) public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private List<AuthorizationServerConfigurer> configurers = Collections.emptyList(); @Autowired private ClientDetailsService clientDetailsService; @Autowired private AuthorizationServerEndpointsConfiguration endpoints; @Autowired public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception { for (AuthorizationServerConfigurer configurer : configurers) { configurer.configure(clientDetails); } } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // Over-riding to make sure this.disableLocalConfigureAuthenticationBldr = false // This will ensure that when this configurer builds the AuthenticationManager it will not attempt // to find another 'Global' AuthenticationManager in the ApplicationContext (if available), // and set that as the parent of this 'Local' AuthenticationManager. // This AuthenticationManager should only be wired up with an AuthenticationProvider // composed of the ClientDetailsService (wired in this configuration) for authenticating 'clients' only. } @Override protected void configure(HttpSecurity http) throws Exception { AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer(); FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping(); http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping); 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 (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); } // @formatter:off http .authorizeRequests() .antMatchers(tokenEndpointPath).fullyAuthenticated() .antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess()) .antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess()) .and() .requestMatchers() .antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); // @formatter:on http.setSharedObject(ClientDetailsService.class, clientDetailsService); } protected void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { for (AuthorizationServerConfigurer configurer : configurers) { configurer.configure(oauthServer); } } }
3. EnableResourceServer工具
Oauth2 資源服務器的便利方法,開啓了一個spring security的filter,這個filter經過一個Oauth2的token進行認證請求。使用者應該增長這個註解,並提供一個ResourceServerConfigurer類型的Bean(例如經過ResouceServerConfigurerAdapter)來指定資源(url路徑和資源id)的細節。爲了利用這個filter,你必須在你的應用中的某些地方EnableWebSecurity,或者使用這個註解的地方,或者其餘別的地方。
這個註解建立了一個WebSecurityConfigurerAdapter,且自帶了硬編碼的order=3.在spring中,因爲技術緣由不能當即改變order的順序,所以你必須在你的spring應用中避免使用order=3的其餘WebSecurityConfigurerAdapter。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ResourceServerConfiguration.class) public @interface EnableResourceServer { }
ResourceServerConfiguration
@Override protected void configure(HttpSecurity http) throws Exception { ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer(); 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
從新的兩個方法
1.init
@Override public void init(HttpSecurity http) throws Exception { registerDefaultAuthenticationEntryPoint(http); } @SuppressWarnings("unchecked") private void registerDefaultAuthenticationEntryPoint(HttpSecurity http) { ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http .getConfigurer(ExceptionHandlingConfigurer.class); if (exceptionHandling == null) { return; } ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class); if (contentNegotiationStrategy == null) { contentNegotiationStrategy = new HeaderContentNegotiationStrategy(); } MediaTypeRequestMatcher preferredMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML, MediaType.MULTIPART_FORM_DATA, MediaType.TEXT_XML); preferredMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL)); exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher); }
2.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); } resourcesServerFilter = postProcess(resourcesServerFilter); resourcesServerFilter.setStateless(stateless); // @formatter:off http .authorizeRequests().expressionHandler(expressionHandler) .and() .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class) .exceptionHandling() .accessDeniedHandler(accessDeniedHandler) .authenticationEntryPoint(authenticationEntryPoint); // @formatter:on }
其中OAuth2AuthenticationProcessingFilter:A pre-authentication filter for OAuth2 protected resources. Extracts an OAuth2 token from the incoming request and uses it to populate the Spring Security context with an {@link OAuth2Authentication} (if used in conjunction with an{@link OAuth2AuthenticationManager}).