mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-09 17:34:50 +08:00
增加client_credentials模式
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
package org.hswebframework.web.oauth2.server;
|
||||
|
||||
public interface ClientCredentialGranter extends OAuth2Granter {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -35,4 +35,10 @@ public class OAuth2Client {
|
||||
}
|
||||
}
|
||||
|
||||
public void validateSecret(String secret) {
|
||||
if (StringUtils.isEmpty(secret) || (!secret.equals(this.clientSecret))) {
|
||||
throw new OAuth2Exception(ErrorType.ILLEGAL_CLIENT_SECRET);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.hswebframework.web.oauth2.server;
|
||||
|
||||
|
||||
import org.hswebframework.web.oauth2.server.code.AuthorizationCodeGranter;
|
||||
import org.hswebframework.web.oauth2.server.credential.ClientCredentialGranter;
|
||||
|
||||
public interface OAuth2GrantService {
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package org.hswebframework.web.oauth2.server;
|
||||
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationHolder;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
|
||||
import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
|
||||
import org.hswebframework.web.oauth2.server.auth.ReactiveOAuth2AccessTokenParser;
|
||||
import org.hswebframework.web.oauth2.server.code.AuthorizationCodeGranter;
|
||||
import org.hswebframework.web.oauth2.server.code.DefaultAuthorizationCodeGranter;
|
||||
import org.hswebframework.web.oauth2.server.credential.ClientCredentialGranter;
|
||||
import org.hswebframework.web.oauth2.server.credential.DefaultClientCredentialGranter;
|
||||
import org.hswebframework.web.oauth2.server.impl.CompositeOAuth2GrantService;
|
||||
import org.hswebframework.web.oauth2.server.impl.RedisAccessTokenManager;
|
||||
import org.hswebframework.web.oauth2.server.web.OAuth2AuthorizeController;
|
||||
@@ -45,6 +48,12 @@ public class OAuth2ServerAutoConfiguration {
|
||||
return new RedisAccessTokenManager(redisConnectionFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ClientCredentialGranter clientCredentialGranter(ReactiveAuthenticationManager authenticationManager,
|
||||
AccessTokenManager accessTokenManager) {
|
||||
return new DefaultClientCredentialGranter(authenticationManager, accessTokenManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.hswebframework.web.oauth2.server.credential;
|
||||
|
||||
import org.hswebframework.web.oauth2.server.AccessToken;
|
||||
import org.hswebframework.web.oauth2.server.OAuth2Granter;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
public interface ClientCredentialGranter extends OAuth2Granter {
|
||||
|
||||
/**
|
||||
* 申请token
|
||||
*
|
||||
* @param request 请求
|
||||
* @return token
|
||||
*/
|
||||
Mono<AccessToken> requestToken(ClientCredentialRequest request);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.hswebframework.web.oauth2.server.credential;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.hswebframework.web.oauth2.server.OAuth2Client;
|
||||
import org.hswebframework.web.oauth2.server.OAuth2Request;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
public class ClientCredentialRequest extends OAuth2Request {
|
||||
|
||||
private final OAuth2Client client;
|
||||
|
||||
public ClientCredentialRequest(OAuth2Client client, Map<String, Object> parameters) {
|
||||
super(parameters);
|
||||
this.client = client;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.hswebframework.web.oauth2.server.credential;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
|
||||
import org.hswebframework.web.oauth2.server.AccessToken;
|
||||
import org.hswebframework.web.oauth2.server.AccessTokenManager;
|
||||
import org.hswebframework.web.oauth2.server.OAuth2Client;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class DefaultClientCredentialGranter implements ClientCredentialGranter {
|
||||
|
||||
private final ReactiveAuthenticationManager authenticationManager;
|
||||
|
||||
private final AccessTokenManager accessTokenManager;
|
||||
|
||||
@Override
|
||||
public Mono<AccessToken> requestToken(ClientCredentialRequest request) {
|
||||
|
||||
OAuth2Client client = request.getClient();
|
||||
|
||||
return authenticationManager
|
||||
.getByUserId(client.getUserId())
|
||||
.flatMap(auth -> accessTokenManager.createAccessToken(client.getClientId(), auth, true));
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package org.hswebframework.web.oauth2.server.impl;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.web.oauth2.server.ClientCredentialGranter;
|
||||
import org.hswebframework.web.oauth2.server.credential.ClientCredentialGranter;
|
||||
import org.hswebframework.web.oauth2.server.OAuth2GrantService;
|
||||
import org.hswebframework.web.oauth2.server.code.AuthorizationCodeGranter;
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ package org.hswebframework.web.oauth2.server.web;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
|
||||
import org.hswebframework.web.authorization.annotation.Authorize;
|
||||
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
|
||||
import org.hswebframework.web.oauth2.ErrorType;
|
||||
@@ -16,6 +18,7 @@ import org.hswebframework.web.oauth2.server.OAuth2ClientManager;
|
||||
import org.hswebframework.web.oauth2.server.OAuth2GrantService;
|
||||
import org.hswebframework.web.oauth2.server.code.AuthorizationCodeRequest;
|
||||
import org.hswebframework.web.oauth2.server.code.AuthorizationCodeTokenRequest;
|
||||
import org.hswebframework.web.oauth2.server.credential.ClientCredentialRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
@@ -37,7 +40,6 @@ public class OAuth2AuthorizeController {
|
||||
|
||||
private final OAuth2ClientManager clientManager;
|
||||
|
||||
|
||||
@GetMapping(value = "/authorize", params = "response_type=code")
|
||||
@Operation(summary = "申请授权码,并获取重定向地址", parameters = {
|
||||
@Parameter(name = "client_id", required = true),
|
||||
@@ -67,30 +69,46 @@ public class OAuth2AuthorizeController {
|
||||
}));
|
||||
}
|
||||
|
||||
@GetMapping(value = "/token", params = "grant_type=authorization_code")
|
||||
@Operation(summary = "使用授权码申请token", parameters = {
|
||||
@GetMapping(value = "/token")
|
||||
@Operation(summary = "使用申请token", parameters = {
|
||||
@Parameter(name = "client_id"),
|
||||
@Parameter(name = "client_secret"),
|
||||
@Parameter(name = "code"),
|
||||
@Parameter(name = "grant_type", description = "固定值为authorization_code")
|
||||
@Parameter(name = "code", description = "grantType为authorization_code时不能为空"),
|
||||
@Parameter(name = "grant_type", schema = @Schema(implementation = GrantType.class))
|
||||
})
|
||||
@Authorize(ignore = true)
|
||||
public Mono<ResponseEntity<AccessToken>> requestTokenByCode(ServerWebExchange exchange) {
|
||||
public Mono<ResponseEntity<AccessToken>> requestTokenByCode(
|
||||
@RequestParam("grant_type") GrantType grantType,
|
||||
ServerWebExchange exchange) {
|
||||
Map<String, String> params = exchange.getRequest().getQueryParams().toSingleValueMap();
|
||||
|
||||
return doRequestCode(new HashMap<>(params))
|
||||
return this
|
||||
.getOAuth2Client(params.get("client_id"))
|
||||
.doOnNext(client -> client.validateSecret(params.get("client_secret")))
|
||||
.flatMap(client -> grantType.requestToken(oAuth2GrantService, client, new HashMap<>(params)))
|
||||
.map(ResponseEntity::ok);
|
||||
}
|
||||
|
||||
private Mono<AccessToken> doRequestCode(Map<String, Object> param) {
|
||||
return this
|
||||
.getOAuth2Client((String) param.get("client_id"))
|
||||
.switchIfEmpty(Mono.error(() -> new OAuth2Exception(ErrorType.ILLEGAL_CLIENT_ID)))
|
||||
.flatMap(client -> oAuth2GrantService
|
||||
public enum GrantType {
|
||||
authorization_code {
|
||||
@Override
|
||||
Mono<AccessToken> requestToken(OAuth2GrantService service, OAuth2Client client, Map<String, Object> param) {
|
||||
return service
|
||||
.authorizationCode()
|
||||
.requestToken(new AuthorizationCodeTokenRequest(client, param)));
|
||||
}
|
||||
.requestToken(new AuthorizationCodeTokenRequest(client, param));
|
||||
}
|
||||
},
|
||||
client_credentials {
|
||||
@Override
|
||||
Mono<AccessToken> requestToken(OAuth2GrantService service, OAuth2Client client, Map<String, Object> param) {
|
||||
return service
|
||||
.clientCredential()
|
||||
.requestToken(new ClientCredentialRequest(client, param));
|
||||
}
|
||||
};
|
||||
|
||||
abstract Mono<AccessToken> requestToken(OAuth2GrantService service, OAuth2Client client, Map<String, Object> param);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String urlEncode(String url) {
|
||||
@@ -108,8 +126,8 @@ public class OAuth2AuthorizeController {
|
||||
return redirectUri + "?" + paramsString;
|
||||
}
|
||||
|
||||
|
||||
private Mono<OAuth2Client> getOAuth2Client(String id) {
|
||||
return clientManager.getClient(id);
|
||||
return clientManager
|
||||
.getClient(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,23 @@ public class RedisAccessTokenManagerTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshToken() {
|
||||
RedisAccessTokenManager tokenManager = new RedisAccessTokenManager(RedisHelper.factory);
|
||||
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
tokenManager
|
||||
.createAccessToken("test", authentication, false)
|
||||
.zipWhen(token -> tokenManager.refreshAccessToken("test", token.getRefreshToken()))
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(tp2 -> {
|
||||
return tp2.getT1().getRefreshToken().equals(tp2.getT2().getRefreshToken());
|
||||
})
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSingletonAccessToken() {
|
||||
RedisAccessTokenManager tokenManager = new RedisAccessTokenManager(RedisHelper.factory);
|
||||
|
||||
Reference in New Issue
Block a user