spring security webflux 三方用户授权说明
spring security 默认整合了github、google、facebook、okta三方登录功能,直接配置client-id、client-secret等参数就可自动登录;
其他三方授权方(如gitee、微博等)需要实现相关的接口
*************************
相关类及接口
CommonOAuth2Provider:默认集成的三方授权提供方
public enum CommonOAuth2Provider {
GOOGLE {
public Builder getBuilder(String registrationId) {
Builder builder = this.getBuilder(registrationId, ClientAuthenticationMethod.BASIC, "{baseUrl}/{action}/oauth2/code/{registrationId}");
builder.scope(new String[]{"openid", "profile", "email"});
builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
builder.tokenUri("https://www.googleapis.com/oauth2/v4/token");
builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs");
builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo");
builder.userNameAttributeName("sub");
builder.clientName("Google");
return builder;
}
},
GITHUB {
public Builder getBuilder(String registrationId) {
Builder builder = this.getBuilder(registrationId, ClientAuthenticationMethod.BASIC, "{baseUrl}/{action}/oauth2/code/{registrationId}");
builder.scope(new String[]{"read:user"});
builder.authorizationUri("https://github.com/login/oauth/authorize");
builder.tokenUri("https://github.com/login/oauth/access_token");
builder.userInfoUri("https://api.github.com/user");
builder.userNameAttributeName("id");
builder.clientName("GitHub");
return builder;
}
},
FACEBOOK {
public Builder getBuilder(String registrationId) {
Builder builder = this.getBuilder(registrationId, ClientAuthenticationMethod.POST, "{baseUrl}/{action}/oauth2/code/{registrationId}");
builder.scope(new String[]{"public_profile", "email"});
builder.authorizationUri("https://www.facebook.com/v2.8/dialog/oauth");
builder.tokenUri("https://graph.facebook.com/v2.8/oauth/access_token");
builder.userInfoUri("https://graph.facebook.com/me?fields=id,name,email");
builder.userNameAttributeName("id");
builder.clientName("Facebook");
return builder;
}
},
OKTA {
public Builder getBuilder(String registrationId) {
Builder builder = this.getBuilder(registrationId, ClientAuthenticationMethod.BASIC, "{baseUrl}/{action}/oauth2/code/{registrationId}");
builder.scope(new String[]{"openid", "profile", "email"});
builder.userNameAttributeName("sub");
builder.clientName("Okta");
return builder;
}
};
private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}";
private CommonOAuth2Provider() {
}
protected final Builder getBuilder(String registrationId, ClientAuthenticationMethod method, String redirectUri) {
Builder builder = ClientRegistration.withRegistrationId(registrationId);
builder.clientAuthenticationMethod(method);
builder.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE);
builder.redirectUriTemplate(redirectUri);
return builder;
}
public abstract Builder getBuilder(String var1);
}
ServerHttpSecurity:oauth2配置入口类
public class ServerHttpSecurity {
******************
内部类:ServerHttpSecurity.OAuth2LoginSpec
public class OAuth2LoginSpec {
private ReactiveClientRegistrationRepository clientRegistrationRepository; //存储授权提供方
private ServerOAuth2AuthorizedClientRepository authorizedClientRepository; //存储授权客户端
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository; //授权请求操作
private ReactiveAuthenticationManager authenticationManager; //获取access_token,成功后加载OAuth2User
private ServerSecurityContextRepository securityContextRepository;
private ServerAuthenticationConverter authenticationConverter;
private ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
private ServerWebExchangeMatcher authenticationMatcher;
private ServerAuthenticationSuccessHandler authenticationSuccessHandler;
private ServerAuthenticationFailureHandler authenticationFailureHandler;
public ServerHttpSecurity.OAuth2LoginSpec authenticationManager(ReactiveAuthenticationManager authenticationManager) {
public ServerHttpSecurity.OAuth2LoginSpec securityContextRepository(ServerSecurityContextRepository securityContextRepository) {
public ServerHttpSecurity.OAuth2LoginSpec authenticationSuccessHandler(ServerAuthenticationSuccessHandler authenticationSuccessHandler) {
public ServerHttpSecurity.OAuth2LoginSpec authenticationFailureHandler(ServerAuthenticationFailureHandler authenticationFailureHandler) {
private ReactiveAuthenticationManager getAuthenticationManager() {
if (this.authenticationManager == null) {
this.authenticationManager = this.createDefault();
}//如果没有设置authenticationManager,则创建默认的authenticationManager
return this.authenticationManager;
}
private ReactiveAuthenticationManager createDefault() {
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> client = this.getAccessTokenResponseClient();
//获取access_token客户端,如果不存在则创建WebClientReactiveAuthorizationCodeTokenResponseClient
ReactiveAuthenticationManager result = new OAuth2LoginReactiveAuthenticationManager(client, this.getOauth2UserService());
//创建OAuth2LoginReactiveAuthenticationManager,该对象可同时获取access_token、oauth2User(如果不存在,默认为DefaultReactiveOAuth2UserService对象)
boolean oidcAuthenticationProviderEnabled = ClassUtils.isPresent("org.springframework.security.oauth2.jwt.JwtDecoder", this.getClass().getClassLoader());
if (oidcAuthenticationProviderEnabled) {
OidcAuthorizationCodeReactiveAuthenticationManager oidc = new OidcAuthorizationCodeReactiveAuthenticationManager(client, this.getOidcUserService());
ResolvableType type = ResolvableType.forClassWithGenerics(ReactiveJwtDecoderFactory.class, new Class[]{ClientRegistration.class});
ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = (ReactiveJwtDecoderFactory)ServerHttpSecurity.this.getBeanOrNull(type);
if (jwtDecoderFactory != null) {
oidc.setJwtDecoderFactory(jwtDecoderFactory);
}
result = new DelegatingReactiveAuthenticationManager(new ReactiveAuthenticationManager[]{oidc, (ReactiveAuthenticationManager)result});
}
return (ReactiveAuthenticationManager)result;
}
public ServerHttpSecurity.OAuth2LoginSpec authenticationConverter(ServerAuthenticationConverter authenticationConverter) {
this.authenticationConverter = authenticationConverter;
return this;
}
private ServerAuthenticationConverter getAuthenticationConverter(ReactiveClientRegistrationRepository clientRegistrationRepository) {
if (this.authenticationConverter == null) {
ServerOAuth2AuthorizationCodeAuthenticationTokenConverter authenticationConverter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository);
authenticationConverter.setAuthorizationRequestRepository(this.getAuthorizationRequestRepository());
this.authenticationConverter = authenticationConverter;
}
return this.authenticationConverter;
}
public ServerHttpSecurity.OAuth2LoginSpec clientRegistrationRepository(ReactiveClientRegistrationRepository clientRegistrationRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
return this;
}
public ServerHttpSecurity.OAuth2LoginSpec authorizedClientService(ReactiveOAuth2AuthorizedClientService authorizedClientService) {
this.authorizedClientRepository = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
return this;
}
public ServerHttpSecurity.OAuth2LoginSpec authorizedClientRepository(ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
public ServerHttpSecurity.OAuth2LoginSpec authorizationRequestRepository(ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository) {
public ServerHttpSecurity.OAuth2LoginSpec authorizationRequestResolver(ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) {
public ServerHttpSecurity.OAuth2LoginSpec authenticationMatcher(ServerWebExchangeMatcher authenticationMatcher) {
private ServerWebExchangeMatcher getAuthenticationMatcher() {
if (this.authenticationMatcher == null) {
this.authenticationMatcher = this.createAttemptAuthenticationRequestMatcher();
} //如果没有设置authenticationManager,则创建默认的authenticationManager
return this.authenticationMatcher;
}
public ServerHttpSecurity and() {
return ServerHttpSecurity.this;
}
protected void configure(ServerHttpSecurity http) {
ReactiveClientRegistrationRepository clientRegistrationRepository = this.getClientRegistrationRepository(); //获取clientRegistration
ServerOAuth2AuthorizedClientRepository authorizedClientRepository = this.getAuthorizedClientRepository(); //获取authorizedClient
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = this.getRedirectWebFilter(); //认证请求跳转过滤器,获取授权码
ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository = this.getAuthorizationRequestRepository();
oauthRedirectFilter.setAuthorizationRequestRepository(authorizationRequestRepository);
oauthRedirectFilter.setRequestCache(http.requestCache.requestCache);
ReactiveAuthenticationManager manager = this.getAuthenticationManager(); //获取access_token、oauth2User
AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager, authorizedClientRepository);
authenticationFilter.setRequiresAuthenticationMatcher(this.getAuthenticationMatcher());
authenticationFilter.setServerAuthenticationConverter(this.getAuthenticationConverter(clientRegistrationRepository));
authenticationFilter.setAuthenticationSuccessHandler(this.getAuthenticationSuccessHandler(http));
authenticationFilter.setAuthenticationFailureHandler(this.getAuthenticationFailureHandler());
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
this.setDefaultEntryPoints(http);
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
}
private void setDefaultEntryPoints(ServerHttpSecurity http) {
String defaultLoginPage = "/login";
Map<String, String> urlToText = http.oauth2Login.getLinks();
String providerLoginPage = null;
if (urlToText.size() == 1) {
providerLoginPage = (String)urlToText.keySet().iterator().next();
}
MediaTypeServerWebExchangeMatcher htmlMatcher = new MediaTypeServerWebExchangeMatcher(new MediaType[]{MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML, MediaType.TEXT_PLAIN});
htmlMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
ServerWebExchangeMatcher xhrMatcher = (exchange) -> {
return exchange.getRequest().getHeaders().getOrEmpty("X-Requested-With").contains("XMLHttpRequest") ? MatchResult.match() : MatchResult.notMatch();
};
ServerWebExchangeMatcher notXhrMatcher = new NegatedServerWebExchangeMatcher(xhrMatcher);
ServerWebExchangeMatcher defaultEntryPointMatcher = new AndServerWebExchangeMatcher(new ServerWebExchangeMatcher[]{notXhrMatcher, htmlMatcher});
if (providerLoginPage != null) {
ServerWebExchangeMatcher loginPageMatcher = new PathPatternParserServerWebExchangeMatcher(defaultLoginPage);
ServerWebExchangeMatcher faviconMatcher = new PathPatternParserServerWebExchangeMatcher("/favicon.ico");
ServerWebExchangeMatcher defaultLoginPageMatcher = new AndServerWebExchangeMatcher(new ServerWebExchangeMatcher[]{new OrServerWebExchangeMatcher(new ServerWebExchangeMatcher[]{loginPageMatcher, faviconMatcher}), defaultEntryPointMatcher});
ServerWebExchangeMatcher matcher = new AndServerWebExchangeMatcher(new ServerWebExchangeMatcher[]{notXhrMatcher, new NegatedServerWebExchangeMatcher(defaultLoginPageMatcher)});
RedirectServerAuthenticationEntryPoint entryPoint = new RedirectServerAuthenticationEntryPoint(providerLoginPage);
entryPoint.setRequestCache(http.requestCache.requestCache);
http.defaultEntryPoints.add(new DelegateEntry(matcher, entryPoint));
}
RedirectServerAuthenticationEntryPoint defaultEntryPoint = new RedirectServerAuthenticationEntryPoint(defaultLoginPage);
defaultEntryPoint.setRequestCache(http.requestCache.requestCache);
http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint));
}
private ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler(ServerHttpSecurity http) {
if (this.authenticationSuccessHandler == null) {
RedirectServerAuthenticationSuccessHandler handler = new RedirectServerAuthenticationSuccessHandler();
handler.setRequestCache(http.requestCache.requestCache);
this.authenticationSuccessHandler = handler;
}
return this.authenticationSuccessHandler;
}
private ServerAuthenticationFailureHandler getAuthenticationFailureHandler() {
if (this.authenticationFailureHandler == null) {
this.authenticationFailureHandler = new RedirectServerAuthenticationFailureHandler("/login?error");
}
return this.authenticationFailureHandler;
}
private ServerWebExchangeMatcher createAttemptAuthenticationRequestMatcher() {
return new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}");
}
private ReactiveOAuth2UserService<OidcUserRequest, OidcUser> getOidcUserService() {
ResolvableType type = ResolvableType.forClassWithGenerics(ReactiveOAuth2UserService.class, new Class[]{OidcUserRequest.class, OidcUser.class});
ReactiveOAuth2UserService<OidcUserRequest, OidcUser> bean = (ReactiveOAuth2UserService)ServerHttpSecurity.this.getBeanOrNull(type);
return (ReactiveOAuth2UserService)(bean == null ? new OidcReactiveOAuth2UserService() : bean);
}
private ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> getOauth2UserService() {
//获取认证用户操作类
ResolvableType type = ResolvableType.forClassWithGenerics(ReactiveOAuth2UserService.class, new Class[]{OAuth2UserRequest.class, OAuth2User.class});
ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> bean = (ReactiveOAuth2UserService)ServerHttpSecurity.this.getBeanOrNull(type);
return (ReactiveOAuth2UserService)(bean == null ? new DefaultReactiveOAuth2UserService() : bean);
}
private Map<String, String> getLinks() {
Iterable<ClientRegistration> registrations = (Iterable)ServerHttpSecurity.this.getBeanOrNull(ResolvableType.forClassWithGenerics(Iterable.class, new Class[]{ClientRegistration.class}));
if (registrations == null) {
return Collections.emptyMap();
} else {
Map<String, String> result = new HashMap();
registrations.iterator().forEachRemaining((r) -> {
String var10000 = (String)result.put("/oauth2/authorization/" + r.getRegistrationId(), r.getClientName());
});
return result;
}
}
private ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> getAccessTokenResponseClient() {
ResolvableType type = ResolvableType.forClassWithGenerics(ReactiveOAuth2AccessTokenResponseClient.class, new Class[]{OAuth2AuthorizationCodeGrantRequest.class});
ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> bean = (ReactiveOAuth2AccessTokenResponseClient)ServerHttpSecurity.this.getBeanOrNull(type);
return (ReactiveOAuth2AccessTokenResponseClient)(bean == null ? new WebClientReactiveAuthorizationCodeTokenResponseClient() : bean);
}
private ReactiveClientRegistrationRepository getClientRegistrationRepository() {
if (this.clientRegistrationRepository == null) {
this.clientRegistrationRepository = (ReactiveClientRegistrationRepository)ServerHttpSecurity.this.getBeanOrNull(ReactiveClientRegistrationRepository.class);
}
return this.clientRegistrationRepository;
}
private OAuth2AuthorizationRequestRedirectWebFilter getRedirectWebFilter() {
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter;
if (this.authorizationRequestResolver == null) {
oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(this.getClientRegistrationRepository());
} else {
oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(this.authorizationRequestResolver);
}
return oauthRedirectFilter;
}
private ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository() {
ServerOAuth2AuthorizedClientRepository result = this.authorizedClientRepository;
if (result == null) {
result = (ServerOAuth2AuthorizedClientRepository)ServerHttpSecurity.this.getBeanOrNull(ServerOAuth2AuthorizedClientRepository.class);
}
if (result == null) {
ReactiveOAuth2AuthorizedClientService authorizedClientService = this.getAuthorizedClientService();
if (authorizedClientService != null) {
result = new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
}
}
return (ServerOAuth2AuthorizedClientRepository)result;
}
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> getAuthorizationRequestRepository() {
if (this.authorizationRequestRepository == null) {
this.authorizationRequestRepository = new WebSessionOAuth2ServerAuthorizationRequestRepository();
}
return this.authorizationRequestRepository;
}
private ReactiveOAuth2AuthorizedClientService getAuthorizedClientService() {
ReactiveOAuth2AuthorizedClientService service = (ReactiveOAuth2AuthorizedClientService)ServerHttpSecurity.this.getBeanOrNull(ReactiveOAuth2AuthorizedClientService.class);
if (service == null) {
service = new InMemoryReactiveOAuth2AuthorizedClientService(this.getClientRegistrationRepository());
}
return (ReactiveOAuth2AuthorizedClientService)service;
}
private OAuth2LoginSpec() {
}
}
*****************
ReactiveClientRegistrationRepository
ReactiveClientRegistrationRepository:存储授权提供方
public interface ReactiveClientRegistrationRepository {
Mono<ClientRegistration> findByRegistrationId(String var1);
}
InMemoryReactiveClientRegistrationRepository:内存中存储clientRegistration
public final class InMemoryReactiveClientRegistrationRepository implements ReactiveClientRegistrationRepository, Iterable<ClientRegistration> {
private final Map<String, ClientRegistration> clientIdToClientRegistration;
public InMemoryReactiveClientRegistrationRepository(ClientRegistration... registrations) {
this(toList(registrations));
}
private static List<ClientRegistration> toList(ClientRegistration... registrations) {
Assert.notEmpty(registrations, "registrations cannot be null or empty");
return Arrays.asList(registrations);
}
public InMemoryReactiveClientRegistrationRepository(List<ClientRegistration> registrations) {
this.clientIdToClientRegistration = toUnmodifiableConcurrentMap(registrations);
}
public Mono<ClientRegistration> findByRegistrationId(String registrationId) {
return Mono.justOrEmpty(this.clientIdToClientRegistration.get(registrationId));
}
public Iterator<ClientRegistration> iterator() {
return this.clientIdToClientRegistration.values().iterator();
}
private static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {
Assert.notEmpty(registrations, "registrations cannot be null or empty");
ConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap();
Iterator var2 = registrations.iterator();
while(var2.hasNext()) {
ClientRegistration registration = (ClientRegistration)var2.next();
Assert.notNull(registration, "no registration can be null");
if (result.containsKey(registration.getRegistrationId())) {
throw new IllegalStateException(String.format("Duplicate key %s", registration.getRegistrationId()));
}
result.put(registration.getRegistrationId(), registration);
}
return Collections.unmodifiableMap(result);
}
}
clientRegistration:clientRegistration属性
public final class ClientRegistration implements Serializable {
private static final long serialVersionUID = 530L;
private String registrationId;
private String clientId;
private String clientSecret;
private ClientAuthenticationMethod clientAuthenticationMethod;
private AuthorizationGrantType authorizationGrantType;
private String redirectUriTemplate;
private Set<String> scopes;
private ClientRegistration.ProviderDetails providerDetails;
private String clientName;
***********
内部类:ClientRegistration.ProviderDetails
public class ProviderDetails implements Serializable {
private static final long serialVersionUID = 530L;
private String authorizationUri;
private String tokenUri;
private ClientRegistration.ProviderDetails.UserInfoEndpoint userInfoEndpoint;
private String jwkSetUri;
private Map<String, Object> configurationMetadata;
*****************
authenticationManager
ReactiveAuthenticationManager:认证authentication接口
@FunctionalInterface
public interface ReactiveAuthenticationManager {
Mono<Authentication> authenticate(Authentication var1);
}
OAuth2LoginReactiveAuthenticationManager:认证autentication,认证成功后加载认证用户
public class OAuth2LoginReactiveAuthenticationManager implements ReactiveAuthenticationManager {
private final ReactiveAuthenticationManager authorizationCodeManager;
private final ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService;
private GrantedAuthoritiesMapper authoritiesMapper = (authorities) -> {
return authorities;
};
public OAuth2LoginReactiveAuthenticationManager(ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient, ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> userService) {
Assert.notNull(accessTokenResponseClient, "accessTokenResponseClient cannot be null");
Assert.notNull(userService, "userService cannot be null");
this.authorizationCodeManager = new OAuth2AuthorizationCodeReactiveAuthenticationManager(accessTokenResponseClient);
this.userService = userService;
}
public Mono<Authentication> authenticate(Authentication authentication) {
//认证authentication
return Mono.defer(() -> {
OAuth2AuthorizationCodeAuthenticationToken token = (OAuth2AuthorizationCodeAuthenticationToken)authentication;
return token.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid") ? Mono.empty() : this.authorizationCodeManager.authenticate(token).onErrorMap(OAuth2AuthorizationException.class, (e) -> {
return new OAuth2AuthenticationException(e.getError(), e.getError().toString());
}).cast(OAuth2AuthorizationCodeAuthenticationToken.class).flatMap(this::onSuccess);
});
}
private Mono<OAuth2LoginAuthenticationToken> onSuccess(OAuth2AuthorizationCodeAuthenticationToken authentication) {
//认证成功后,加载认证用户
OAuth2AccessToken accessToken = authentication.getAccessToken();
Map<String, Object> additionalParameters = authentication.getAdditionalParameters();
OAuth2UserRequest userRequest = new OAuth2UserRequest(authentication.getClientRegistration(), accessToken, additionalParameters);
return this.userService.loadUser(userRequest).map((oauth2User) -> {
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper.mapAuthorities(oauth2User.getAuthorities());
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(authentication.getClientRegistration(), authentication.getAuthorizationExchange(), oauth2User, mappedAuthorities, accessToken, authentication.getRefreshToken());
return authenticationResult;
});
}
}
ReactiveOAuth2UserService:加载认证用户接口
@FunctionalInterface
public interface ReactiveOAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {
Mono<U> loadUser(R var1) throws OAuth2AuthenticationException;
}
DefaultReactiveOAuth2UserService:默认加载认证用户实现类
public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserService<OAuth2UserRequest, OAuth2User> {
private static final String INVALID_USER_INFO_RESPONSE_ERROR_CODE = "invalid_user_info_response";
private static final String MISSING_USER_INFO_URI_ERROR_CODE = "missing_user_info_uri";
private static final String MISSING_USER_NAME_ATTRIBUTE_ERROR_CODE = "missing_user_name_attribute";
private WebClient webClient = WebClient.create();
public DefaultReactiveOAuth2UserService() {
}
public Mono<OAuth2User> loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
//加载认证用户
return Mono.defer(() -> {
Assert.notNull(userRequest, "userRequest cannot be null");
String userInfoUri = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();
if (!StringUtils.hasText(userInfoUri)) {
OAuth2Error oauth2Error = new OAuth2Error("missing_user_info_uri", "Missing required UserInfo Uri in UserInfoEndpoint for Client Registration: " + userRequest.getClientRegistration().getRegistrationId(), (String)null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} else {
String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();
if (!StringUtils.hasText(userNameAttributeName)) {
OAuth2Error oauth2Errorx = new OAuth2Error("missing_user_name_attribute", "Missing required \"user name\" attribute name in UserInfoEndpoint for Client Registration: " + userRequest.getClientRegistration().getRegistrationId(), (String)null);
throw new OAuth2AuthenticationException(oauth2Errorx, oauth2Errorx.toString());
} else {
ParameterizedTypeReference<Map<String, Object>> typeReference = new ParameterizedTypeReference<Map<String, Object>>() {
};
AuthenticationMethod authenticationMethod = userRequest.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getAuthenticationMethod();
RequestHeadersSpec requestHeadersSpec;
if (AuthenticationMethod.FORM.equals(authenticationMethod)) {
requestHeadersSpec = ((RequestBodySpec)((RequestBodySpec)((RequestBodySpec)this.webClient.post().uri(userInfoUri, new Object[0])).header("Accept", new String[]{"application/json"})).header("Content-Type", new String[]{"application/x-www-form-urlencoded"})).syncBody("access_token=" + userRequest.getAccessToken().getTokenValue());
} else {
requestHeadersSpec = this.webClient.get().uri(userInfoUri, new Object[0]).header("Accept", new String[]{"application/json"}).headers((headers) -> {
headers.setBearerAuth(userRequest.getAccessToken().getTokenValue());
});
}
Mono<Map<String, Object>> userAttributes = requestHeadersSpec.retrieve().onStatus((s) -> {
return s != HttpStatus.OK;
}, (response) -> {
return parse(response).map((userInfoErrorResponse) -> {
String description = userInfoErrorResponse.getErrorObject().getDescription();
OAuth2Error oauth2Error = new OAuth2Error("invalid_user_info_response", description, (String)null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
});
}).bodyToMono(typeReference);
return userAttributes.map((attrs) -> {
GrantedAuthority authority = new OAuth2UserAuthority(attrs);
Set<GrantedAuthority> authorities = new HashSet();
authorities.add(authority);
OAuth2AccessToken token = userRequest.getAccessToken();
Iterator var6 = token.getScopes().iterator();
while(var6.hasNext()) {
String scope = (String)var6.next();
authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
}
return new DefaultOAuth2User(authorities, attrs, userNameAttributeName);
}).onErrorMap((e) -> {
return e instanceof IOException;
}, (t) -> {
return new AuthenticationServiceException("Unable to access the userInfoEndpoint " + userInfoUri, t);
}).onErrorMap((t) -> {
return !(t instanceof AuthenticationServiceException);
}, (t) -> {
OAuth2Error oauth2Error = new OAuth2Error("invalid_user_info_response", "An error occurred reading the UserInfo Success response: " + t.getMessage(), (String)null);
return new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString(), t);
});
}
}
});
}
public void setWebClient(WebClient webClient) {
Assert.notNull(webClient, "webClient cannot be null");
this.webClient = webClient;
}
private static Mono<UserInfoErrorResponse> parse(ClientResponse httpResponse) {
String wwwAuth = httpResponse.headers().asHttpHeaders().getFirst("WWW-Authenticate");
if (!StringUtils.isEmpty(wwwAuth)) {
return Mono.fromCallable(() -> {
return UserInfoErrorResponse.parse(wwwAuth);
});
} else {
ParameterizedTypeReference<Map<String, String>> typeReference = new ParameterizedTypeReference<Map<String, String>>() {
};
return httpResponse.bodyToMono(typeReference).map((body) -> {
return new UserInfoErrorResponse(ErrorObject.parse(new JSONObject(body)));
});
}
}
}
*****************
获取 access_token
WebClientReactiveAuthorizationCodeTokenResponseClient:获取access_token的客户端
public class WebClientReactiveAuthorizationCodeTokenResponseClient extends AbstractWebClientReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> {
public WebClientReactiveAuthorizationCodeTokenResponseClient() {
}
ClientRegistration clientRegistration(OAuth2AuthorizationCodeGrantRequest grantRequest) {
return grantRequest.getClientRegistration();
}
Set<String> scopes(OAuth2AuthorizationCodeGrantRequest grantRequest) {
return Collections.emptySet();
}
Set<String> defaultScopes(OAuth2AuthorizationCodeGrantRequest grantRequest) {
return grantRequest.getAuthorizationExchange().getAuthorizationRequest().getScopes();
}
FormInserter<String> populateTokenRequestBody(OAuth2AuthorizationCodeGrantRequest grantRequest, FormInserter<String> body) {
super.populateTokenRequestBody(grantRequest, body);
OAuth2AuthorizationExchange authorizationExchange = grantRequest.getAuthorizationExchange();
OAuth2AuthorizationResponse authorizationResponse = authorizationExchange.getAuthorizationResponse();
body.with("code", authorizationResponse.getCode());
String redirectUri = authorizationExchange.getAuthorizationRequest().getRedirectUri();
if (redirectUri != null) {
body.with("redirect_uri", redirectUri);
}
String codeVerifier = (String)authorizationExchange.getAuthorizationRequest().getAttribute("code_verifier");
if (codeVerifier != null) {
body.with("code_verifier", codeVerifier);
}
return body;
}
}
AbstractWebClientOAuth2ReactiveAccessTokenResponseClient:使用webClient获取acccess_token
abstract class AbstractWebClientReactiveOAuth2AccessTokenResponseClient<T extends AbstractOAuth2AuthorizationGrantRequest> implements ReactiveOAuth2AccessTokenResponseClient<T> {
private WebClient webClient = WebClient.builder().build();
AbstractWebClientReactiveOAuth2AccessTokenResponseClient() {
}
public Mono<OAuth2AccessTokenResponse> getTokenResponse(T grantRequest) {
Assert.notNull(grantRequest, "grantRequest cannot be null");
return Mono.defer(() -> {
return ((RequestBodySpec)((RequestBodySpec)this.webClient.post().uri(this.clientRegistration(grantRequest).getProviderDetails().getTokenUri(), new Object[0])).headers((headers) -> {
this.populateTokenRequestHeaders(grantRequest, headers);
})).body(this.createTokenRequestBody(grantRequest)).exchange().flatMap((response) -> {
return this.readTokenResponse(grantRequest, response);
});
});
}
abstract ClientRegistration clientRegistration(T var1);
private void populateTokenRequestHeaders(T grantRequest, HttpHeaders headers) {
ClientRegistration clientRegistration = this.clientRegistration(grantRequest);
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
if (ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {
headers.setBasicAuth(clientRegistration.getClientId(), clientRegistration.getClientSecret());
}
}
private FormInserter<String> createTokenRequestBody(T grantRequest) {
FormInserter<String> body = BodyInserters.fromFormData("grant_type", grantRequest.getGrantType().getValue());
return this.populateTokenRequestBody(grantRequest, body);
}
FormInserter<String> populateTokenRequestBody(T grantRequest, FormInserter<String> body) {
ClientRegistration clientRegistration = this.clientRegistration(grantRequest);
if (!ClientAuthenticationMethod.BASIC.equals(clientRegistration.getClientAuthenticationMethod())) {
body.with("client_id", clientRegistration.getClientId());
}
if (ClientAuthenticationMethod.POST.equals(clientRegistration.getClientAuthenticationMethod())) {
body.with("client_secret", clientRegistration.getClientSecret());
}
Set<String> scopes = this.scopes(grantRequest);
if (!CollectionUtils.isEmpty(scopes)) {
body.with("scope", StringUtils.collectionToDelimitedString(scopes, " "));
}
return body;
}
abstract Set<String> scopes(T var1);
Set<String> defaultScopes(T grantRequest) {
return this.scopes(grantRequest);
}
private Mono<OAuth2AccessTokenResponse> readTokenResponse(T grantRequest, ClientResponse response) {
return ((Mono)response.body(OAuth2BodyExtractors.oauth2AccessTokenResponse())).map((tokenResponse) -> {
return this.populateTokenResponse(grantRequest, tokenResponse);
});
}
OAuth2AccessTokenResponse populateTokenResponse(T grantRequest, OAuth2AccessTokenResponse tokenResponse) {
if (CollectionUtils.isEmpty(tokenResponse.getAccessToken().getScopes())) {
Set<String> defaultScopes = this.defaultScopes(grantRequest);
tokenResponse = OAuth2AccessTokenResponse.withResponse(tokenResponse).scopes(defaultScopes).build();
}
return tokenResponse;
}
public void setWebClient(WebClient webClient) {
Assert.notNull(webClient, "webClient cannot be null");
this.webClient = webClient;
}
}
|