关于springsecurity:spring-securityoauth2-实现统一认证鉴权平台客户端sdk部分

接入简略,反对多租户,可配置,低代码式的疾速实现一套本人的认证鉴权逻辑

蕴含用户治理、部门治理、角色治理、权限治理,将来还能够反对更多模块,比方数据字典治理、…

预计节俭每个利用相干模块研发工夫5人天

对于oauth2.0协定来说,有几个概念

  1. 受权服务器
  2. 客户端
  3. 资源服务器

这偏文章次要讲客户端sdk实现,资源服务器次要就是对立寄存用户信息的,受权服务器应用springsecurity官网的进行小定制。

首先应用springboot的主动配置性能,次要配置类有SecurityConfig,通过编写META-INF/spring.factories来实现主动加载配置bean


@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableConfigurationProperties(OauthProperties.class)
public class SecurityConfig {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain oauth2SecurityFilterChain(HttpSecurity http,
                                                         IntrospectorFilter filter,
                                                         CustomLoginUrlAuthenticationEntryPoint entryPoint,
                                                         ClientRegistrationRepository clientRegistrationRepository,
                                                         CustomRequestCache customRequestCache) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry = http.authorizeRequests();
        if(!CollectionUtils.isEmpty(oauthProperties.getIgnoreUris())) {
            expressionInterceptUrlRegistry.requestMatchers(oauthProperties.getIgnoreUris().stream().map(AntPathRequestMatcher::new).toArray(RequestMatcher[]::new)).permitAll();
        }
        expressionInterceptUrlRegistry.anyRequest().authenticated().and()
                .oauth2Login().authorizationEndpoint().authorizationRequestResolver(authorizationRequestResolver(clientRegistrationRepository))
                .and()
                // .defaultSuccessUrl("/", true);
                .successHandler(authenticationSuccessHandler());
            http.oauth2Client().and()
                .cors().and()
                .exceptionHandling().authenticationEntryPoint(entryPoint).and()
                    .csrf().disable()
                    .requestCache(requestCacheCustomizer->{
                        requestCacheCustomizer.requestCache(customRequestCache.getRequestCache(http));
                    });
        http.addFilterBefore(filter, AnonymousAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    public CustomRequestCache customRequestCache() {
        return new CustomRequestCache();
    };

    private AuthenticationSuccessHandler authenticationSuccessHandler() {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new CustomSavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setUseReferer(true);
        return successHandler;
    }

    private OAuth2AuthorizationRequestResolver authorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
        DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =
                new DefaultOAuth2AuthorizationRequestResolver(
                        clientRegistrationRepository, "/oauth2/authorization");
        authorizationRequestResolver.setAuthorizationRequestCustomizer(
                authorizationRequestCustomizer());

        return  authorizationRequestResolver;
    }

    private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
        return customizer -> customizer
                .additionalParameters(params -> params.put("tenantId", oauthProperties.getClientId()));
    }

    @Bean
    public CustomLoginUrlAuthenticationEntryPoint customLoginUrlAuthenticationEntryPoint(ObjectMapper objectMapper) {
        CustomLoginUrlAuthenticationEntryPoint customLoginUrlAuthenticationEntryPoint = new CustomLoginUrlAuthenticationEntryPoint("/oauth2/authorization/auth_server");
        customLoginUrlAuthenticationEntryPoint.setObjectMapper(objectMapper);
        customLoginUrlAuthenticationEntryPoint.setOauthProperties(oauthProperties);
        return customLoginUrlAuthenticationEntryPoint;
    };

    @Bean
    public IntrospectorFilter introspectorFilter(OpaqueTokenIntrospector opaqueTokenIntrospector,
                                                 OAuth2AuthorizedClientService oAuth2AuthorizedClientService) {
        IntrospectorFilter introspectorFilter = new IntrospectorFilter();
        introspectorFilter.setOpaqueTokenIntrospector(opaqueTokenIntrospector);
        introspectorFilter.setoAuth2AuthorizedClientService(oAuth2AuthorizedClientService);
        return introspectorFilter;
    }

    @Bean
    public NimbusOpaqueTokenIntrospector opaqueTokenIntrospector() {
        String introspectUri = oauthProperties.getIssuerUri() + "/oauth2/introspect";
        OAuth2ResourceServerProperties.Opaquetoken opaqueToken = new OAuth2ResourceServerProperties.Opaquetoken();
        opaqueToken.setIntrospectionUri(introspectUri);
        opaqueToken.setClientId(oauthProperties.getClientId());
        opaqueToken.setClientSecret(oauthProperties.getClientSecret());
        return new NimbusOpaqueTokenIntrospector(opaqueToken.getIntrospectionUri(), opaqueToken.getClientId(),
                opaqueToken.getClientSecret());
    }

    //@RefreshScope 动静刷新
    @Bean
    @ConditionalOnMissingBean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(this.clientRegistration());
    }

    @Bean
    public OidcUserService oidcUserService(DefaultOAuth2UserService defaultOAuth2UserService) {
        CustomOidcUserService oidcUserService = new CustomOidcUserService();
        oidcUserService.setAccessibleScopes0(scopes);
        oidcUserService.setOauth2UserService0(defaultOAuth2UserService);
        return oidcUserService;
    }

    @ConditionalOnMissingBean
    @Bean
    public ParameterizedTypeReference<Result<OpenAppUser<Map<String, Object>>>> userInfoTypeReference () {
        return new ParameterizedTypeReference<Result<OpenAppUser<Map<String, Object>>>>() {};
    }

    @Bean
    public DefaultOAuth2UserService defaultOAuth2UserService(ParameterizedTypeReference userInfoTypeReference) {
        CustomDefaultOAuth2UserService defaultOAuth2UserService = new CustomDefaultOAuth2UserService();
        defaultOAuth2UserService.setPARAMETERIZED_RESPONSE_TYPE(userInfoTypeReference);
        return defaultOAuth2UserService;
    }

    @Autowired
    OauthProperties oauthProperties;

    private Set<String> scopes = new HashSet(){
        {
            //add("openid");
            add("user");
        }
    };

    private ClientRegistration clientRegistration() {
        if (oauthProperties.getScopes()!=null) scopes.addAll(oauthProperties.getScopes());
        ClientRegistration.Builder auth_server = ClientRegistrations.fromIssuerLocation(oauthProperties.getIssuerUri()).registrationId("auth_server");
        return auth_server.clientId(oauthProperties.getClientId())
                .clientSecret(oauthProperties.getClientSecret())
                .clientName(oauthProperties.getClientName())
                //.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                //.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .redirectUri((StringUtils.hasText(oauthProperties.getServerUrl())?oauthProperties.getServerUrl():"{baseUrl}")+"/login/oauth2/code/{registrationId}")
                .scope(scopes)
                .userInfoUri(oauthProperties.getIssuerUri()+"/client/userinfo")
                .userNameAttributeName(IdTokenClaimNames.SUB)
                .build();
    }

}

springsecurity对于oauth2.0的反对还是比较完善的,然而不完全符合咱们对认证受权零碎的要求,所以我这里做了很多自定义,能够说把大部分的拦截器都实现了一些,发现security的代码品质也不是很高,很多中央没有做到扩展性,只能重写类。

而后须要接入利用进行配置,所以定义了一个properties


@ConfigurationProperties(prefix = "oauth")
@Data
public class OauthProperties {

    /**
     * 客户端id
     */
    private String clientId;

    /**
     * 客户端密钥
     */
    private String clientSecret;

    /**
     * 客户端名称
     */
    private String clientName;

    /**
     * 接入受权服务器配置uri
     */
    private String issuerUri;

    /**
     * 权限 默认有openid,user
     */
    private Collection<String> scopes;

    /**
     * 过滤url白名单
     */
    private Collection<String> ignoreUris;

    /**
     * 服务部署地址(可空,默认读取数据库对应的回调地址)
     */
    private String serverUrl;

}

这样接入利用就能够通过配置文件自定义了。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理