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

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

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

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