替换默认的客户端授权处理类,以方便获取客户端的 scope

This commit is contained in:
b2baccline
2021-09-15 22:01:08 +08:00
parent 235adeefe4
commit 9872a38171
6 changed files with 157 additions and 23 deletions

View File

@@ -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 中存放一些敏感信息,比如数据权限相关属性

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
/**

View File

@@ -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")

View File

@@ -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;