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

42次阅读

共计 6514 个字符,预计需要花费 17 分钟才能阅读完成。

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

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

预计节俭每个利用相干模块研发工夫 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;

}

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

正文完
 0