diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java deleted file mode 100644 index cee9c7ca6..000000000 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.hswebframework.web.oauth2.server; - -public interface ClientCredentialGranter extends OAuth2Granter { - - - -} diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java index 6421546de..d231171a5 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java @@ -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); + } + } + } diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java index 0b1feebf1..e723f1632 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java @@ -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 { diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ServerAutoConfiguration.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ServerAutoConfiguration.java index 51c979d50..e2cd3205d 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ServerAutoConfiguration.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ServerAutoConfiguration.java @@ -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 diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/ClientCredentialGranter.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/ClientCredentialGranter.java new file mode 100644 index 000000000..140421bcb --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/ClientCredentialGranter.java @@ -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 requestToken(ClientCredentialRequest request); + + +} diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/ClientCredentialRequest.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/ClientCredentialRequest.java new file mode 100644 index 000000000..2579909f9 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/ClientCredentialRequest.java @@ -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 parameters) { + super(parameters); + this.client = client; + } +} diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/DefaultClientCredentialGranter.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/DefaultClientCredentialGranter.java new file mode 100644 index 000000000..74155bc44 --- /dev/null +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/credential/DefaultClientCredentialGranter.java @@ -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 requestToken(ClientCredentialRequest request) { + + OAuth2Client client = request.getClient(); + + return authenticationManager + .getByUserId(client.getUserId()) + .flatMap(auth -> accessTokenManager.createAccessToken(client.getClientId(), auth, true)); + } +} diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/impl/CompositeOAuth2GrantService.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/impl/CompositeOAuth2GrantService.java index c4d3ec5ad..5b37bb7d9 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/impl/CompositeOAuth2GrantService.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/impl/CompositeOAuth2GrantService.java @@ -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; diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/web/OAuth2AuthorizeController.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/web/OAuth2AuthorizeController.java index dc30e1dd7..b30caa38b 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/web/OAuth2AuthorizeController.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/web/OAuth2AuthorizeController.java @@ -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> requestTokenByCode(ServerWebExchange exchange) { + public Mono> requestTokenByCode( + @RequestParam("grant_type") GrantType grantType, + ServerWebExchange exchange) { Map 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 doRequestCode(Map 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 requestToken(OAuth2GrantService service, OAuth2Client client, Map param) { + return service .authorizationCode() - .requestToken(new AuthorizationCodeTokenRequest(client, param))); - } + .requestToken(new AuthorizationCodeTokenRequest(client, param)); + } + }, + client_credentials { + @Override + Mono requestToken(OAuth2GrantService service, OAuth2Client client, Map param) { + return service + .clientCredential() + .requestToken(new ClientCredentialRequest(client, param)); + } + }; + abstract Mono requestToken(OAuth2GrantService service, OAuth2Client client, Map param); + } @SneakyThrows public static String urlEncode(String url) { @@ -108,8 +126,8 @@ public class OAuth2AuthorizeController { return redirectUri + "?" + paramsString; } - private Mono getOAuth2Client(String id) { - return clientManager.getClient(id); + return clientManager + .getClient(id); } } diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/test/java/org/hswebframework/web/oauth2/server/impl/RedisAccessTokenManagerTest.java b/hsweb-authorization/hsweb-authorization-oauth2/src/test/java/org/hswebframework/web/oauth2/server/impl/RedisAccessTokenManagerTest.java index 6be2ee561..75f65ac82 100644 --- a/hsweb-authorization/hsweb-authorization-oauth2/src/test/java/org/hswebframework/web/oauth2/server/impl/RedisAccessTokenManagerTest.java +++ b/hsweb-authorization/hsweb-authorization-oauth2/src/test/java/org/hswebframework/web/oauth2/server/impl/RedisAccessTokenManagerTest.java @@ -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);