spring-security-oauth2注解詳解


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}).

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM