配置 hosts
shell
127.0.0.1 nacos
127.0.0.1 mysql
127.0.0.1 redis
127.0.0.1 kibana
127.0.0.1 rocketmq
127.0.0.1 admin.elsfs.test
启动oauth2
启动 Oauth2ApplicationStarter 注意: oauth2_registered_client数据库需要配置
sql
INSERT INTO oauth2_registered_client (id, client_id, client_id_issued_at, client_secret,
client_secret_expires_at, client_name,
client_authentication_methods, authorization_grant_types,
redirect_uris, post_logout_redirect_uris, scopes, client_settings,
token_settings)
VALUES ('5', 'messaging-client', '2023-12-02 18:48:22', '$2a$10$hVq5XfeMHERLoFo6RBUFieyrZF8ElwvRRgrig/wb/IkXUzz3zCZhG',
null, '5', 'client_secret_basic', 'refresh_token,authorization_code',
'http://admin.elsfs.test:7001/login/oauth2/code/login-client,http://127.0.0.1:7001/authorized,https://www.baidu.com',
'', 'openid,profile,message.read,message.write',
'{"@class":"java.util.Collections$UnmodifiableMap","settings.client.require-proof-key":false,"settings.client.require-authorization-consent":true}', '{
"@class": "java.util.Collections$UnmodifiableMap",
"settings.token.reuse-refresh-tokens": true,
"settings.token.id-token-signature-algorithm": [
"org.springframework.security.oauth2.jose.jws.SignatureAlgorithm",
"RS256"
],
"settings.token.access-token-time-to-live": [
"java.time.Duration",
3600.000000000
],
"settings.token.access-token-format": {
"@class": "org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat",
"value": "self-contained"
},
"settings.token.refresh-token-time-to-live": [
"java.time.Duration",
3600.000000000
],
"settings.token.authorization-code-time-to-live": [
"java.time.Duration",
300.000000000
],
"settings.token.device-code-time-to-live": [
"java.time.Duration",
300.000000000
]
}');
elsfs-cloud-stater 配置文件新增如下配置 一定要和数据库一致,
提示
为什么使用 http://admin.elsfs.test:7002?如果两边都使用
yaml
spring:
security:
oauth2:
client:
registration:
login-client:
# http://127.0.0.1:7001/oauth2/authorization/login-client
# 然后跳转到http://mysql:7002/login
provider: spring
client-id: messaging-client
# "$2a$10$hVq5XfeMHERLoFo6RBUFieyrZF8ElwvRRgrig/wb/IkXUzz3zCZhG"
client-secret: secret
client-authentication-method: client_secret_basic
authorization-grant-type: authorization_code
redirect-uri: http://127.0.0.1:7001/login/oauth2/code/login-client
scope:
- openid
- message.read
- message.write
client-name: spring
provider:
spring:
user-name-attribute: sub
token-uri: http://admin.elsfs.test:7002/oauth2/token
user-info-uri: http://admin.elsfs.test:7002/userinfo
authorization-uri: http://admin.elsfs.test:7002/oauth2/authorize
jwk-set-uri: http://admin.elsfs.test:7002/oauth2/jwks
第一部: 访问: http://127.0.0.1:7001/oauth2/authorization/login-client 跳转到: http://admin.elsfs.test:7002/login
获取 code 后的 OAuth2LoginAuthenticationFilter 源码
java
public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// 获取 code,state
MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap());
// 判断是否包含 code 和state
if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) {
OAuth2Error oauth2Error = new OAuth2Error("invalid_request");
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} else {
// 删除授权请求
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response);
if (authorizationRequest == null) {
OAuth2Error oauth2Error = new OAuth2Error("authorization_request_not_found");
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} else {
// 获取 注册 id login-client
String registrationId = (String)authorizationRequest.getAttribute("registration_id");
// 获取 客户端注册信息
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
if (clientRegistration == null) {
OAuth2Error oauth2Error = new OAuth2Error("client_registration_not_found", "Client Registration not found with Id: " + registrationId, (String)null);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} else {
// 构建 重定向 url http://127.0.0.1:7001/login/oauth2/code/login-client
String redirectUri = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)).replaceQuery((String)null).build().toUriString();
// 构建授权响应
OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri);
Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request);
// 通过 OidcAuthorizationCodeAuthenticationProvider 获取 OAuth2登录身份验证令牌
OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken(clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse));
authenticationRequest.setDetails(authenticationDetails);
// 获取登录成功的 令牌
OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken)this.getAuthenticationManager().authenticate(authenticationRequest);
// oauth2身份验证成功令牌
OAuth2AuthenticationToken oauth2Authentication = (OAuth2AuthenticationToken)this.authenticationResultConverter.convert(authenticationResult);
Assert.notNull(oauth2Authentication, "authentication result cannot be null");
oauth2Authentication.setDetails(authenticationDetails);
// 获取授权客户
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(authenticationResult.getClientRegistration(), oauth2Authentication.getName(), authenticationResult.getAccessToken(), authenticationResult.getRefreshToken());
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, oauth2Authentication, request, response);
return oauth2Authentication;
}
}
}
}
}
OidcAuthorizationCodeAuthenticationProvider 部分
java
public class OidcAuthorizationCodeAuthenticationProvider implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken)authentication;
// 判断是否授权范围包含 openid
if (!authorizationCodeAuthentication.getAuthorizationExchange().getAuthorizationRequest().getScopes().contains("openid")) {
return null;
} else {
OAuth2AuthorizationRequest authorizationRequest = authorizationCodeAuthentication.getAuthorizationExchange().getAuthorizationRequest();
OAuth2AuthorizationResponse authorizationResponse = authorizationCodeAuthentication.getAuthorizationExchange().getAuthorizationResponse();
if (authorizationResponse.statusError()) {
throw new OAuth2AuthenticationException(authorizationResponse.getError(), authorizationResponse.getError().toString());
// 判断State是否一致
} else if (!authorizationResponse.getState().equals(authorizationRequest.getState())) {
OAuth2Error oauth2Error = new OAuth2Error("invalid_state_parameter");
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
} else {
// DefaultAuthorizationCodeTokenResponseClient ->getTokenResponse 获取 accessToken,refreshToken,和id_token
OAuth2AccessTokenResponse accessTokenResponse = this.getResponse(authorizationCodeAuthentication);
// 获取注册的客户端
ClientRegistration clientRegistration = authorizationCodeAuthentication.getClientRegistration();
Map<String, Object> additionalParameters = accessTokenResponse.getAdditionalParameters();
//判断是否包含 id_token
if (!additionalParameters.containsKey("id_token")) {
OAuth2Error invalidIdTokenError = new OAuth2Error("invalid_id_token", "Missing (required) ID Token in Token Response for Client Registration: " + clientRegistration.getRegistrationId(), (String)null);
throw new OAuth2AuthenticationException(invalidIdTokenError, invalidIdTokenError.toString());
} else {
// 通过jwtDecoder(jwt解码器)->OidcIdTokenDecoderFactory 创建Oidc令牌
OidcIdToken idToken = this.createOidcToken(clientRegistration, accessTokenResponse);
// 验证随机数 nonce ,nonce自行百度
this.validateNonce(authorizationRequest, idToken);
// 通过 OidcUserService 获取oidcUser
OidcUser oidcUser = (OidcUser)this.userService.loadUser(new OidcUserRequest(clientRegistration, accessTokenResponse.getAccessToken(), idToken, additionalParameters));
// 默认转换权限前面加“OIDC_”
Collection<? extends GrantedAuthority> mappedAuthorities = this.authoritiesMapper.mapAuthorities(oidcUser.getAuthorities());
// 设置验证成功
OAuth2LoginAuthenticationToken authenticationResult = new OAuth2LoginAuthenticationToken(authorizationCodeAuthentication.getClientRegistration(), authorizationCodeAuthentication.getAuthorizationExchange(), oidcUser, mappedAuthorities, accessTokenResponse.getAccessToken(), accessTokenResponse.getRefreshToken());
authenticationResult.setDetails(authorizationCodeAuthentication.getDetails());
return authenticationResult;
}
}
}
}
}
FederatedIdentityAuthenticationSuccessHandler
java
@Override
public void onAuthenticationSuccess(
HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
if (authentication instanceof OAuth2AuthenticationToken auth2AuthenticationToken) {
// 获取授权的名字
String identifier = auth2AuthenticationToken.getName();
// 授权的客户端名称
String identityType = auth2AuthenticationToken.getAuthorizedClientRegistrationId();
LOGGER.debug("oauth2Login登录成功,登录账号:{},登录类型:{}", identifier, identityType);
// 断言是否要处理关联存储和绑定
if (federatedIdentityPredicate.handlePredicate(identifier, identityType)) {
registerLogicHandler.registerLogic(request, response, auth2AuthenticationToken);
return;
}
}
this.delegate.onAuthenticationSuccess(request, response, authentication);
}