接入简略,反对多租户,可配置,低代码式的疾速实现一套本人的认证鉴权逻辑
蕴含用户治理、部门治理、角色治理、权限治理,将来还能够反对更多模块,比方数据字典治理、...
预计节俭每个利用相干模块研发工夫5人天
对于oauth2.0协定来说,有几个概念
- 受权服务器
- 客户端
- 资源服务器
这偏文章次要讲客户端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;}
这样接入利用就能够通过配置文件自定义了。