spring-security-oauth2支持的注解有:
1.EnableOAuth2Client
适用于使用spring security,并且想从Oauth2认证服务器来获取授权的web应用环境代码中,它启用了一个Oauth2 客户端配置。为了更好的利用这个特性,需要在客户端应用中的DelegatingFilterProxy(代理一个名为oauth2ClientContextFilter)增加一个servlet filter。当filter配置到client app时,可以使用注解@AccessTokenRequest提供的另一个bean来创建一个Oauth2RequestTemplate。示例:
@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. EnableAuthorizationServer
工具方法,用来在当前应用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(必须)。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class) public @interface EnableAuthorizationServer
2.1 AuthorizationServerEndpointsConfiguration
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 AuthorizationServerSecurityConfiguration
@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).