Browse Source

增加client_credentials模式

zhou-hao 4 years ago
parent
commit
3a0de51a17

+ 0 - 7
hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java

@@ -1,7 +0,0 @@
-package org.hswebframework.web.oauth2.server;
-
-public interface ClientCredentialGranter extends OAuth2Granter {
-
-
-
-}

+ 6 - 0
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);
+        }
+    }
+
 }

+ 1 - 0
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 {
 

+ 9 - 0
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

+ 18 - 0
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<AccessToken> requestToken(ClientCredentialRequest request);
+
+
+}

+ 18 - 0
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<String, Object> parameters) {
+        super(parameters);
+        this.client = client;
+    }
+}

+ 26 - 0
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<AccessToken> requestToken(ClientCredentialRequest request) {
+
+        OAuth2Client client = request.getClient();
+
+        return authenticationManager
+                .getByUserId(client.getUserId())
+                .flatMap(auth -> accessTokenManager.createAccessToken(client.getClientId(), auth, true));
+    }
+}

+ 1 - 1
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;
 

+ 34 - 16
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,31 +69,47 @@ 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) {
         return URLEncoder.encode(url, "utf-8");
@@ -108,8 +126,8 @@ public class OAuth2AuthorizeController {
         return redirectUri + "?" + paramsString;
     }
 
-
     private Mono<OAuth2Client> getOAuth2Client(String id) {
-        return clientManager.getClient(id);
+        return clientManager
+                .getClient(id);
     }
 }

+ 17 - 0
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);