⚡ 替换默认的客户端授权处理类,以方便获取客户端的 scope
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.hccake.ballcat.auth;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.hccake.ballcat.common.security.userdetails.ClientPrincipal;
|
||||
import com.hccake.ballcat.common.security.userdetails.User;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@@ -31,6 +32,14 @@ public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {
|
||||
response.put("scope", CollectionUtil.join(scopes, " "));
|
||||
}
|
||||
|
||||
// 是否是客户端
|
||||
boolean isClient = authentication.getPrincipal().getClass().isAssignableFrom(ClientPrincipal.class);
|
||||
response.put("is_client", isClient);
|
||||
if (isClient) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// TODO 使用 Scope 进行校验
|
||||
// 默认的 CustomTokenEnhancer 在登录获取 token 时只在 attribute 中存放了 ROLE 和 PERMISSION
|
||||
// 如果是自己系统内部认可的远程 资源服务器,在拥有权限的情况下,把所有的属性都返回回去
|
||||
// 因为实际业务中,可能会在 attributes 中存放一些敏感信息,比如数据权限相关属性
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hccake.ballcat.auth.authentication;
|
||||
|
||||
import com.hccake.ballcat.common.security.userdetails.ClientPrincipal;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Request;
|
||||
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
|
||||
import org.springframework.security.oauth2.provider.TokenRequest;
|
||||
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* client_credentials 客户端凭证模式的授权处理器
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
public class CustomClientCredentialsTokenGranter extends ClientCredentialsTokenGranter {
|
||||
|
||||
public CustomClientCredentialsTokenGranter(AuthorizationServerTokenServices tokenServices,
|
||||
ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
|
||||
super(tokenServices, clientDetailsService, requestFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||
|
||||
OAuth2Request oAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||
|
||||
ClientPrincipal clientPrincipal = new ClientPrincipal(oAuth2Request.getClientId(), new HashMap<>(8),
|
||||
client.getAuthorities());
|
||||
clientPrincipal.setScope(client.getScope());
|
||||
|
||||
OAuth2ClientAuthenticationToken userAuthentication = new OAuth2ClientAuthenticationToken(clientPrincipal, null);
|
||||
|
||||
return new OAuth2Authentication(oAuth2Request, userAuthentication);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hccake.ballcat.auth.authentication;
|
||||
|
||||
import com.hccake.ballcat.common.security.userdetails.ClientPrincipal;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation used for OAuth 2.0 Client Authentication.
|
||||
*
|
||||
* @author hccake
|
||||
* @see AbstractAuthenticationToken
|
||||
*/
|
||||
public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final ClientPrincipal clientPrincipal;
|
||||
|
||||
private final Object credentials;
|
||||
|
||||
public OAuth2ClientAuthenticationToken(ClientPrincipal clientPrincipal, @Nullable Object credentials) {
|
||||
super(Collections.emptyList());
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.credentials = credentials;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.clientPrincipal;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.hccake.ballcat.auth.configurer;
|
||||
|
||||
import com.hccake.ballcat.auth.mobile.MobileTokenGranter;
|
||||
import com.hccake.ballcat.auth.authentication.CustomClientCredentialsTokenGranter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -14,17 +14,24 @@ import org.springframework.security.oauth2.config.annotation.configurers.ClientD
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
|
||||
import org.springframework.security.oauth2.provider.TokenGranter;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
|
||||
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -94,18 +101,32 @@ public class CustomAuthorizationServerConfigurer implements AuthorizationServerC
|
||||
// 自定义的认证时异常转换
|
||||
.exceptionTranslator(webResponseExceptionTranslator)
|
||||
// 自定义tokenGranter
|
||||
.tokenGranter(tokenGranter(endpoints));
|
||||
.tokenGranter(tokenGranter(endpoints))
|
||||
// 使用自定义的 TokenConverter,方便在 checkToken 时,返回更多的信息
|
||||
.accessTokenConverter(accessTokenConverter);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
// 使用自定义的 TokenConverter,方便在 checkToken 时,返回更多的信息
|
||||
endpoints.accessTokenConverter(accessTokenConverter);
|
||||
// 获取默认的granter集合
|
||||
List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
|
||||
granters.add(new MobileTokenGranter(authenticationManager, endpoints.getTokenServices(),
|
||||
endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
|
||||
return new CompositeTokenGranter(granters);
|
||||
// OAuth2 规范的四大授权类型
|
||||
ClientDetailsService clientDetailsService = endpoints.getClientDetailsService();
|
||||
AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
|
||||
AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
|
||||
OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();
|
||||
|
||||
List<TokenGranter> tokenGranters = new ArrayList<>();
|
||||
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices,
|
||||
clientDetailsService, requestFactory));
|
||||
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetailsService, requestFactory));
|
||||
ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetailsService, requestFactory);
|
||||
tokenGranters.add(implicit);
|
||||
tokenGranters.add(new CustomClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory));
|
||||
if (authenticationManager != null) {
|
||||
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
|
||||
clientDetailsService, requestFactory));
|
||||
}
|
||||
|
||||
return new CompositeTokenGranter(tokenGranters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,22 +19,31 @@ import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.hccake.ballcat.common.security.constant.TokenAttributeNameConstants;
|
||||
import com.hccake.ballcat.common.security.userdetails.ClientPrincipal;
|
||||
import com.hccake.ballcat.common.security.userdetails.User;
|
||||
import com.nimbusds.oauth2.sdk.ParseException;
|
||||
import com.nimbusds.oauth2.sdk.TokenIntrospectionResponse;
|
||||
import com.nimbusds.oauth2.sdk.TokenIntrospectionSuccessResponse;
|
||||
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
|
||||
import com.nimbusds.oauth2.sdk.id.Audience;
|
||||
import net.minidev.json.JSONArray;
|
||||
import net.minidev.json.JSONObject;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.*;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.BadOpaqueTokenException;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionException;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -44,7 +53,12 @@ import org.springframework.web.client.RestTemplate;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -62,7 +76,7 @@ import java.util.*;
|
||||
*/
|
||||
public class RemoteOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Converter<String, RequestEntity<?>> requestEntityConverter;
|
||||
|
||||
@@ -198,6 +212,9 @@ public class RemoteOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
}
|
||||
claims.put(OAuth2IntrospectionClaimNames.AUDIENCE, Collections.unmodifiableList(audiences));
|
||||
}
|
||||
if (response.getClientID() != null) {
|
||||
claims.put(OAuth2IntrospectionClaimNames.CLIENT_ID, response.getClientID().getValue());
|
||||
}
|
||||
if (response.getExpirationTime() != null) {
|
||||
Instant exp = response.getExpirationTime().toInstant();
|
||||
claims.put(OAuth2IntrospectionClaimNames.EXPIRES_AT, exp);
|
||||
@@ -218,14 +235,16 @@ public class RemoteOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
||||
claims.put(OAuth2IntrospectionClaimNames.SCOPE, scopes);
|
||||
}
|
||||
|
||||
boolean isClient = response.getClientID() != null;
|
||||
if (isClient) {
|
||||
claims.put(OAuth2IntrospectionClaimNames.CLIENT_ID, response.getClientID().getValue());
|
||||
return buildClient(claims);
|
||||
boolean isClient;
|
||||
try {
|
||||
isClient = response.getBooleanParameter("is_client");
|
||||
}
|
||||
else {
|
||||
return buildUser(response.toJSONObject(), claims);
|
||||
catch (ParseException e) {
|
||||
logger.warn("自定端点返回的 is_client 属性解析异常: {}, 请求信息:[{}]", e.getMessage(), response.toJSONObject());
|
||||
isClient = false;
|
||||
}
|
||||
return isClient ? buildClient(claims) : buildUser(response.toJSONObject(), claims);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -11,7 +12,7 @@ import java.util.*;
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
public class ClientPrincipal implements OAuth2AuthenticatedPrincipal {
|
||||
public class ClientPrincipal implements OAuth2AuthenticatedPrincipal, Serializable {
|
||||
|
||||
private final String clientId;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user