增加client_credentials模式

This commit is contained in:
zhou-hao
2020-10-26 11:29:22 +08:00
parent 223e9b9d91
commit 3a0de51a17
10 changed files with 130 additions and 24 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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