【Spring Security】 授权服务 AuthorizationServerCConfigurerAdapter 配置类

Metadata

title: 【Spring Security】 授权服务 AuthorizationServerCConfigurerAdapter 配置类
date: 2023-02-02 22:28
tags:
  - 行动阶段/完成
  - 主题场景/组件
  - 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
  - 细化主题/Module/SpringSecurity
categories:
  - SpringSecurity
keywords:
  - SpringSecurity
description: 【Spring Security】 授权服务 AuthorizationServerCConfigurerAdapter 配置类

【Spring Security】 授权服务 AuthorizationServerCConfigurerAdapter 配置类

AuthorizationServerConfigurer

AuthorizationServerConfigurer 是配置 OAUth2 授权服务器的配置类接口,添加了 @EnableAuthorizationServer,spring 会自动注入。接口有三个方法,可以实现客户端配置、安全功能、以及各个 Endpoint(端点)的相关配置。

public interface AuthorizationServerConfigurer {

    /**
     * 配置授权服务器的安全性
     * @param security 安全功能配置器
     */
    void configure(AuthorizationServerSecurityConfigurer security) throws Exception;

    /**
     * ClientDetailsS​​ervice配置
     * @param 客户端信息配置器
     */
    void configure(ClientDetailsServiceConfigurer clients) throws Exception;

    /**
     * 配置授权服务器端点的非安全功能,如令牌存储、令牌定制、用户批准和授权类型。默认情况下你不需要做任何事情,除非你需要密码授权,在这种情况下你需要提供一个 {@link AuthenticationManager}。
     * @param 端点配置器
     */
    void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception;

}

OAuth2AuthorizationServerConfiguration

OAuth2AuthorizationServerConfiguration 是 AuthorizationServerConfigurer 默认的实现类,他是 Spring Security OAuth2 授权服务器的默认配置,当没有自定义配置时,将会使用此配置。

@Configuration
@ConditionalOnClass(EnableAuthorizationServer.class)
@ConditionalOnMissingBean(AuthorizationServerConfigurer.class)
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)
@EnableConfigurationProperties(AuthorizationServerProperties.class)
@Import(AuthorizationServerTokenServicesConfiguration.class)
public class OAuth2AuthorizationServerConfiguration
        extends AuthorizationServerConfigurerAdapter {

    private static final Log logger = LogFactory
            .getLog(OAuth2AuthorizationServerConfiguration.class);
    // ClientDetails基础实现类
    private final BaseClientDetails details;
    // 认证管理器,password模式需要
    private final AuthenticationManager authenticationManager;
    // 保存 OAuth2 token ,有InMemoryTokenStore、JdbcTokenStore、JwkTokenStore、RedisTokenStore。
    private final TokenStore tokenStore;
    // 用于将身份验证数据存储在令牌内的令牌服务实现的转换器接口, 有DefaultAccessTokenConverter、JwtAccessTokenConverter。
    private final AccessTokenConverter tokenConverter;
    //  OAuth2 Authorization server配置
    private final AuthorizationServerProperties properties;
    // 构造方法
    public OAuth2AuthorizationServerConfiguration(BaseClientDetails details,
            AuthenticationConfiguration authenticationConfiguration,
            ObjectProvider<TokenStore> tokenStore,
            ObjectProvider<AccessTokenConverter> tokenConverter,
            AuthorizationServerProperties properties) throws Exception {
        this.details = details;
        this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
        this.tokenStore = tokenStore.getIfAvailable();
        this.tokenConverter = tokenConverter.getIfAvailable();
        this.properties = properties;
    }
    // ClientDetailsService 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 使用ClientDetailsServiceBuilder
        ClientDetailsServiceBuilder<InMemoryClientDetailsServiceBuilder>.ClientBuilder builder = clients
                // 在内存中创建一个Oauth2 Client
                .inMemory().withClient(this.details.getClientId());
                // 密码
        builder.secret(this.details.getClientSecret())
                // 资源ID集合
                .resourceIds(this.details.getResourceIds().toArray(new String[0]))
                // 授权类型集合
                .authorizedGrantTypes(
                        this.details.getAuthorizedGrantTypes().toArray(new String[0]))
                // 授权
                .authorities(
                        AuthorityUtils.authorityListToSet(this.details.getAuthorities())
                                .toArray(new String[0]))
                // 授权范围集合
                .scopes(this.details.getScope().toArray(new String[0]));
        // 自动授权
        if (this.details.getAutoApproveScopes() != null) {
            builder.autoApprove(
                    this.details.getAutoApproveScopes().toArray(new String[0]));
        }
        // accessToken过期时间
        if (this.details.getAccessTokenValiditySeconds() != null) {
            builder.accessTokenValiditySeconds(
                    this.details.getAccessTokenValiditySeconds());
        }
        // RefreshToke过期时间
        if (this.details.getRefreshTokenValiditySeconds() != null) {
            builder.refreshTokenValiditySeconds(
                    this.details.getRefreshTokenValiditySeconds());
        }
        // 回调地址集合
        if (this.details.getRegisteredRedirectUri() != null) {
            builder.redirectUris(
                    this.details.getRegisteredRedirectUri().toArray(new String[0]));
        }
    }
    // AuthorizationServerEndpoints
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
            throws Exception {
        if (this.tokenConverter != null) {
            // accessToken转换器
            endpoints.accessTokenConverter(this.tokenConverter);
        }
        if (this.tokenStore != null) {
            // token存储器
            endpoints.tokenStore(this.tokenStore);
        }
        // 密码模式,添加认证管理器
        if (this.details.getAuthorizedGrantTypes().contains("password")) {
            endpoints.authenticationManager(this.authenticationManager);
        }
    }
    // AuthorizationServerSecurity
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security)
            throws Exception {
        security.passwordEncoder(NoOpPasswordEncoder.getInstance());
        if (this.properties.getCheckTokenAccess() != null) {
            // 检查令牌端点的 Spring Security 访问规则(例如 SpEL 表达式像“isAuthenticated()”)。默认为空,解释为“denyAll()”(无访问权限)。
            security.checkTokenAccess(this.properties.getCheckTokenAccess());
        }
        if (this.properties.getTokenKeyAccess() != null) {
            // 令牌密钥端点的 Spring Security 访问规则(例如,像“isAuthenticated()”这样的 SpEL 表达式)。默认为空,被解释为“denyAll()”(无访问访问)。
            security.tokenKeyAccess(this.properties.getTokenKeyAccess());
        }
        if (this.properties.getRealm() != null) {
            // 用于客户端身份验证的领域名称。如果未经身份验证的请求进入令牌端点,它将以包含此名称的质询进行响应
            security.realm(this.properties.getRealm());
        }
    }

    @Configuration
    protected static class ClientDetailsLogger implements InitializingBean {

        private final OAuth2ClientProperties credentials;

        protected ClientDetailsLogger(OAuth2ClientProperties credentials) {
            this.credentials = credentials;
        }

        public void afterPropertiesSet() {
            init();
        }

        public void init() {
            String prefix = "security.oauth2.client";
            boolean defaultSecret = this.credentials.isDefaultSecret();
            logger.info(String.format(
                    "Initialized OAuth2 Client%n%n%s.client-id = %s%n"
                            + "%s.client-secret = %s%n%n",
                    prefix, this.credentials.getClientId(), prefix,
                    defaultSecret ? this.credentials.getClientSecret() : "****"));
        }

    }
    
    // 默认的ClientDetails 
    @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.<String>emptySet());
            return details;
        }

    }
}

ClientDetailsServiceConfigurer

ClientDetailsServiceConfigurer 用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。

public class ClientDetailsServiceConfigurer extends
        SecurityConfigurerAdapter<ClientDetailsService, ClientDetailsServiceBuilder<?>> {

    public ClientDetailsServiceConfigurer(ClientDetailsServiceBuilder<?> builder) {
        setBuilder(builder);
    }
    // Builder
    public ClientDetailsServiceBuilder<?> withClientDetails(ClientDetailsService clientDetailsService) throws Exception {
        setBuilder(getBuilder().clients(clientDetailsService));
        return this.and();
    }
    // 内存
    public InMemoryClientDetailsServiceBuilder inMemory() throws Exception {
        InMemoryClientDetailsServiceBuilder next = getBuilder().inMemory();
        setBuilder(next);
        return next;
    }
    // JDBC 
    public JdbcClientDetailsServiceBuilder jdbc(DataSource dataSource) throws Exception {
        JdbcClientDetailsServiceBuilder next = getBuilder().jdbc().dataSource(dataSource);
        setBuilder(next);
        return next;
    }
    
    @Override
    public void init(ClientDetailsServiceBuilder<?> builder) throws Exception {
    }

    @Override
    public void configure(ClientDetailsServiceBuilder<?> builder) throws Exception {
    }

}

AuthorizationServerEndpointsConfigurer

配置授权服务器端点的属性和增强功能,主要用来来配置令牌(token)的访问端点和令牌服务 (token services)。

public final class AuthorizationServerEndpointsConfigurer {
    // 授权服务器令牌,主要是创建令牌、返回
    private AuthorizationServerTokenServices tokenServices;
    //  使用随机 UUID 值作为访问令牌和刷新令牌值的令牌服务的基本实现
    private ConsumerTokenServices consumerTokenServices;
    // 用于发布和存储授权码的服务
    private AuthorizationCodeServices authorizationCodeServices;
    // 资源服务器令牌服务
    private ResourceServerTokenServices resourceTokenServices;
    // OAuth2 令牌的存储服务
    private TokenStore tokenStore;
    // 令牌增强器,在 {@link AuthorizationServerTokenServices} 存储访问令牌之前增强访问令牌的策略
    private TokenEnhancer tokenEnhancer;
    // 令牌转换器,用于将身份验证数据存储在令牌内的令牌服务实现
    private AccessTokenConverter accessTokenConverter;
    // 用于保存、检索和撤销用户批准的界面(每个客户端、每个范围)
    private ApprovalStore approvalStore;
    // 令牌授权器,对应不同的授权模式
    private TokenGranter tokenGranter;
    // 管理 OAuth2 请求的策略
    private OAuth2RequestFactory requestFactory;
    // 对 {@link AuthorizationEndpoint} 和 {@link TokenEndpoint} 的 OAuth2 请求的验证接口。
    private OAuth2RequestValidator requestValidator;
    // 用于确定给定客户端身份验证请求是否已被当前用户批准的处理器
    private UserApprovalHandler userApprovalHandler;
    // Security 认证管理器
    private AuthenticationManager authenticationManager;
    // ClientDetails服务
    private ClientDetailsService clientDetailsService;
    //  HandlerMapping FrameworkEndpoint 前缀
    private String prefix;
    // HandlerMapping 映射关系
    private Map<String, String> patternMap = new HashMap<String, String>();
    // 允许访问端点的请求方式
    private Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet<HttpMethod>();
    // FrameworkEndpointHandlerMapping 
    private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
    // 显式禁用批准存储,即使通常会自动添加一个(通常在不使用 JWT 时)。如果没有批准存储,则只能要求用户批准或拒绝授权,而没有任何更精细的决定
    private boolean approvalStoreDisabled;
    // HandlerMapping 拦截器
    private List<Object> interceptors = new ArrayList<Object>();
    // DefaultTokenServices 
    private DefaultTokenServices defaultTokenServices;
    // UserDetailsService 
    private UserDetailsService userDetailsService;
    // 是否重写tokenService
    private boolean tokenServicesOverride = false;
    // 是否重写userDetailsService
    private boolean userDetailsServiceOverride = false;
    // 重用刷新令牌
    private boolean reuseRefreshToken = true;
    // 将异常转换为 HTTP 响应
    private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator;
    // 确定用户代理的重定向 URI
    private RedirectResolver redirectResolver;
    // 动态代理AuthorizationServerTokenServices
    public AuthorizationServerTokenServices getTokenServices() {
        return ProxyCreator.getProxy(AuthorizationServerTokenServices.class,
                new ObjectFactory<AuthorizationServerTokenServices>() {
                    @Override
                    public AuthorizationServerTokenServices getObject() throws BeansException {
                        return tokenServices();
                    }
                });
    }
    /**
     * The AuthenticationManager for the password grant.
     *  密码模式设置AuthenticationManager 
     * @param authenticationManager an AuthenticationManager, fully initialized
     * @return this for a fluent style
     */
    public AuthorizationServerEndpointsConfigurer authenticationManager(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        return this;
    }

    // 创建默认的tokenServices 
    private DefaultTokenServices createDefaultTokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setReuseRefreshToken(reuseRefreshToken);
        tokenServices.setClientDetailsService(clientDetailsService());
        tokenServices.setTokenEnhancer(tokenEnhancer());
        addUserDetailsService(tokenServices, this.userDetailsService);
        return tokenServices;
    }
    // 用户同意授权处理器
    private UserApprovalHandler userApprovalHandler() {
        if (userApprovalHandler == null) {
            if (approvalStore() != null) {
                ApprovalStoreUserApprovalHandler handler = new ApprovalStoreUserApprovalHandler();
                handler.setApprovalStore(approvalStore());
                handler.setRequestFactory(requestFactory());
                handler.setClientDetailsService(clientDetailsService);
                this.userApprovalHandler = handler;
            }
            else if (tokenStore() != null) {
                TokenStoreUserApprovalHandler userApprovalHandler = new TokenStoreUserApprovalHandler();
                userApprovalHandler.setTokenStore(tokenStore());
                userApprovalHandler.setClientDetailsService(clientDetailsService());
                userApprovalHandler.setRequestFactory(requestFactory());
                this.userApprovalHandler = userApprovalHandler;
            }
            else {
                throw new IllegalStateException("Either a TokenStore or an ApprovalStore must be provided");
            }
        }
        return this.userApprovalHandler;
    }

    // 添加授权类型令牌服务
    private List<TokenGranter> getDefaultTokenGranters() {
        ClientDetailsService clientDetails = clientDetailsService();
        AuthorizationServerTokenServices tokenServices = tokenServices();
        AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
        OAuth2RequestFactory requestFactory = requestFactory();

        List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
        tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,
                requestFactory));
        tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
        ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
        tokenGranters.add(implicit);
        tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
        if (authenticationManager != null) {
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
                    clientDetails, requestFactory));
        }
        return tokenGranters;
    }

    // 使用FrameworkEndpointHandlerMapping可以替换提供的端点。
    private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping() {
        if (frameworkEndpointHandlerMapping == null) {
            frameworkEndpointHandlerMapping = new FrameworkEndpointHandlerMapping();
            frameworkEndpointHandlerMapping.setMappings(patternMap);
            frameworkEndpointHandlerMapping.setPrefix(prefix);
            frameworkEndpointHandlerMapping.setInterceptors(interceptors.toArray());
        }
        return frameworkEndpointHandlerMapping;
    }
}

AuthorizationServerSecurityConfigurer

AuthorizationServerSecurityConfigurer 在令牌端点上定义了安全约束。

public final class AuthorizationServerSecurityConfigurer extends
        SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    // 但是用户没有通过认证,那么抛出异常,AuthenticationEntryPoint. Commence(..),Oauth为OAuth2AuthenticationEntryPoint
    private AuthenticationEntryPoint authenticationEntryPoint;
    // AccessDenied处理器
    private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
    // client的密码解析器
    private PasswordEncoder passwordEncoder; // for client secrets
    // 
    private String realm = "oauth2/client";
    // 允许客户端进行表单身份验证
    private boolean allowFormAuthenticationForClients = false;
    //  用于检查令牌(/oauth/check_token和/oauth/token_key),这些端点默认受保护denyAll()
    private String tokenKeyAccess = "denyAll()";
    private String checkTokenAccess = "denyAll()";
    // 仅仅支持ssl
    private boolean sslOnly = false;

    /**
     *TokenEndpoint 的自定义身份验证过滤器。过滤器将设置在默认BasicAuthenticationFilter 的上游
     */
    private List<Filter> tokenEndpointAuthenticationFilters = new ArrayList<Filter>();

    @Override
    public void init(HttpSecurity http) throws Exception {

        registerDefaultAuthenticationEntryPoint(http);
        if (passwordEncoder != null) {
            ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService());
            clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder());
            http.getSharedObject(AuthenticationManagerBuilder.class)
                    .userDetailsService(clientDetailsUserDetailsService)
                    .passwordEncoder(passwordEncoder());
        }
        else {
            http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService()));
        }
        http.securityContext().securityContextRepository(new NullSecurityContextRepository()).and().csrf().disable()
                .httpBasic().realmName(realm);
        if (sslOnly) {
            http.requiresChannel().anyRequest().requiresSecure();
        }
    }
    
    // 注册默认身份验证入口点
    @SuppressWarnings("unchecked")
    private void registerDefaultAuthenticationEntryPoint(HttpSecurity http) {
        ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http
                .getConfigurer(ExceptionHandlingConfigurer.class);
        if (exceptionHandling == null) {
            return;
        }
        if (authenticationEntryPoint==null) {
            BasicAuthenticationEntryPoint basicEntryPoint = new BasicAuthenticationEntryPoint();
            basicEntryPoint.setRealmName(realm);
            authenticationEntryPoint = basicEntryPoint;
        }
        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);
    }
}