This is an automated email from the ASF dual-hosted git repository. liubao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/servicecomb-fence.git
commit d16bba4634bd4c548424b45d994cb799e3949f3b Author: liubao <[email protected]> AuthorDate: Mon Jun 17 15:08:29 2019 +0800 [SCB-1319]refactor code to better satisfy session token & jwt token authentication --- .../server/PasswordTokenGranter.java | 38 +++----- .../server/RefreshTokenTokenGranter.java | 36 ++----- .../authentication/server/TokenResponse.java | 32 +++--- .../authentication/token/TokenConfiguration.java | 30 +++--- .../token/AbstractOpenIDTokenStore.java | 52 ++++++++++ .../token/InMemoryOpenIDTokenStore.java | 51 ++++++++++ .../token/InMemorySessionIDTokenStore.java | 42 -------- .../servicecomb/authentication/token/JWTToken.java | 65 +------------ .../token/{JWTToken.java => JWTTokenImpl.java} | 8 +- .../authentication/token/JWTTokenStore.java | 53 +--------- .../{JWTTokenStore.java => JWTTokenStoreImpl.java} | 22 ++--- .../authentication/token/OpenIDToken.java | 108 +++++++++++++++++++++ .../token/{Token.java => OpenIDTokenStore.java} | 19 ++-- ...tSessionIDTokenStore.java => SessionToken.java} | 2 +- .../{SessionIDToken.java => SessionTokenImpl.java} | 15 +-- .../{TokenStore.java => SessionTokenStore.java} | 11 ++- .../servicecomb/authentication/token/Token.java | 2 +- .../authentication/token/TokenStore.java | 6 +- .../servicecomb/authentication/util/Constants.java | 27 ++++++ .../authentication/edge/AuthHandler.java | 41 +++++--- .../authentication/edge/AuthenticationFilter.java | 6 +- ...re.java => DumyEdgeTokenResponseProcessor.java} | 10 +- ...yEdgeTokenStore.java => EdgeConfiguration.java} | 26 ++--- ...nStore.java => EdgeTokenResponseProcessor.java} | 6 +- .../authentication/edge/TokenEndpoint.java | 8 +- .../resource/ResourceAuthHandler.java | 18 ++-- docs/zh_CN/developersGuide.md | 13 +-- .../AuthenticationConfiguration.java | 32 +++--- samples/Client/pom.xml | 9 -- .../authentication/AuthenticationTestCase.java | 29 +++--- .../servicecomb/authentication/TestEndpoint.java | 14 ++- .../apache/servicecomb/authentication/TestMgr.java | 4 +- .../gateway/AuthenticationConfiguration.java | 25 ++++- .../resource/AuthenticationConfiguration.java | 14 +-- 34 files changed, 470 insertions(+), 404 deletions(-) diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java index a1538ff..6f893cd 100644 --- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java +++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/PasswordTokenGranter.java @@ -19,7 +19,9 @@ package org.apache.servicecomb.authentication.server; import java.util.Map; -import org.apache.servicecomb.authentication.token.TokenStore; +import org.apache.servicecomb.authentication.token.AbstractOpenIDTokenStore; +import org.apache.servicecomb.authentication.token.OpenIDToken; +import org.apache.servicecomb.authentication.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.userdetails.UserDetails; @@ -29,27 +31,19 @@ import org.springframework.stereotype.Component; import com.netflix.config.DynamicPropertyFactory; -@Component(value = "passwordTokenGranter") +@Component public class PasswordTokenGranter implements TokenGranter { @Autowired - @Qualifier("authUserDetailsService") + @Qualifier(Constants.BEAN_AUTH_USER_DETAILS_SERVICE) private UserDetailsService userDetailsService; @Autowired - @Qualifier("authPasswordEncoder") + @Qualifier(Constants.BEAN_AUTH_PASSWORD_ENCODER) private PasswordEncoder passwordEncoder; @Autowired - @Qualifier("authAccessTokenStore") - private TokenStore accessTokenStore; - - @Autowired - @Qualifier("authRefreshTokenStore") - private TokenStore refreshTokenStore; - - @Autowired - @Qualifier("authIDTokenStore") - private TokenStore idTokenStore; + @Qualifier(Constants.BEAN_AUTH_OPEN_ID_TOKEN_STORE) + private AbstractOpenIDTokenStore openIDTokenStore; @Override public TokenResponse grant(Map<String, String> parameters) { @@ -58,17 +52,9 @@ public class PasswordTokenGranter implements TokenGranter { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (passwordEncoder.matches(password, userDetails.getPassword())) { - TokenResponse token = new TokenResponse(); - token.setAccess_token(accessTokenStore.createToken(userDetails).getValue()); - token.setRefresh_token(refreshTokenStore.createToken(userDetails).getValue()); - token.setId_token(idTokenStore.createToken(userDetails).getValue()); - - //TODO add parameters. - token.setScope(null); - token.setExpires_in(10 * 60); - token.setToken_type("bearer"); - - return token; + OpenIDToken openIDToken = openIDTokenStore.createToken(userDetails); + openIDTokenStore.saveToken(openIDToken); + return TokenResponse.fromOpenIDToken(openIDToken); } else { return null; } @@ -82,7 +68,7 @@ public class PasswordTokenGranter implements TokenGranter { @Override public boolean enabled() { return DynamicPropertyFactory.getInstance() - .getBooleanProperty("servicecomb.authentication.granter.password.enabled", true) + .getBooleanProperty(Constants.CONFIG_GRANTER_PASSWORD_ENABLED, true) .get(); } diff --git a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java index 6980287..4b0f93f 100644 --- a/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java +++ b/api/authentication-server/endpoint/src/main/java/org/apache/servicecomb/authentication/server/RefreshTokenTokenGranter.java @@ -19,8 +19,10 @@ package org.apache.servicecomb.authentication.server; import java.util.Map; +import org.apache.servicecomb.authentication.token.AbstractOpenIDTokenStore; +import org.apache.servicecomb.authentication.token.OpenIDToken; import org.apache.servicecomb.authentication.token.Token; -import org.apache.servicecomb.authentication.token.TokenStore; +import org.apache.servicecomb.authentication.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.security.core.userdetails.UserDetails; @@ -29,23 +31,15 @@ import org.springframework.stereotype.Component; import com.netflix.config.DynamicPropertyFactory; -@Component(value = "fefreshTokenTokenGranter") +@Component public class RefreshTokenTokenGranter implements TokenGranter { @Autowired - @Qualifier("authUserDetailsService") + @Qualifier(Constants.BEAN_AUTH_USER_DETAILS_SERVICE) private UserDetailsService userDetailsService; @Autowired - @Qualifier("authAccessTokenStore") - private TokenStore accessTokenStore; - - @Autowired - @Qualifier("authRefreshTokenStore") - private TokenStore refreshTokenStore; - - @Autowired - @Qualifier("authIDTokenStore") - private TokenStore idTokenStore; + @Qualifier(Constants.BEAN_AUTH_OPEN_ID_TOKEN_STORE) + private AbstractOpenIDTokenStore openIDTokenStore; @Override public boolean enabled() { @@ -63,22 +57,12 @@ public class RefreshTokenTokenGranter implements TokenGranter { public TokenResponse grant(Map<String, String> parameters) { String refreshTokenValue = parameters.get(TokenConst.PARAM_REFRESH_TOKEN); - Token refreshToken = refreshTokenStore.readTokenByValue(refreshTokenValue); + Token refreshToken = openIDTokenStore.readTokenByRefreshTokenValue(refreshTokenValue); if (refreshToken != null && !refreshToken.isExpired()) { UserDetails userDetails = userDetailsService.loadUserByUsername(refreshToken.username()); - - TokenResponse token = new TokenResponse(); - token.setAccess_token(accessTokenStore.createToken(userDetails).getValue()); - // refresh token is not generated - token.setRefresh_token(refreshTokenValue); - token.setId_token(idTokenStore.createToken(userDetails).getValue()); - - //TODO add parameters. - token.setScope(null); - token.setExpires_in(10 * 60); - token.setToken_type("bearer"); - return token; + OpenIDToken openIDToken = openIDTokenStore.createToken(userDetails); + return TokenResponse.fromOpenIDToken(openIDToken); } return null; } diff --git a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java b/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java index 9bdc6d7..32e7fb5 100644 --- a/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java +++ b/api/authentication-server/service/src/main/java/org/apache/servicecomb/authentication/server/TokenResponse.java @@ -20,6 +20,8 @@ package org.apache.servicecomb.authentication.server; import java.util.Map; import java.util.Set; +import org.apache.servicecomb.authentication.token.OpenIDToken; + public class TokenResponse { // Naming conventions https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-00#section-3.1 private String token_type; @@ -31,13 +33,10 @@ public class TokenResponse { // Naming conventions https://openid.net/specs/openid-connect-basic-1_0.html#ObtainingTokens private String id_token; - private int expires_in; + private long expires_in; private Set<String> scope; - // JWT id - private String jti; - private Map<String, Object> additionalInformation; public String getToken_type() { @@ -64,11 +63,11 @@ public class TokenResponse { this.refresh_token = refresh_token; } - public int getExpires_in() { + public long getExpires_in() { return expires_in; } - public void setExpires_in(int expires_in) { + public void setExpires_in(long expires_in) { this.expires_in = expires_in; } @@ -88,14 +87,6 @@ public class TokenResponse { this.scope = scope; } - public String getJti() { - return jti; - } - - public void setJti(String jti) { - this.jti = jti; - } - public Map<String, Object> getAdditionalInformation() { return additionalInformation; } @@ -104,4 +95,17 @@ public class TokenResponse { this.additionalInformation = additionalInformation; } + public static TokenResponse fromOpenIDToken(OpenIDToken openIDToken) { + TokenResponse token = new TokenResponse(); + token.setAccess_token(openIDToken.getAccessToken().getValue()); + token.setRefresh_token(openIDToken.getRefreshToken().getValue()); + token.setId_token(openIDToken.getIdToken().getValue()); + + token.setAdditionalInformation(openIDToken.getAdditionalInformation()); + token.setScope(openIDToken.getScope()); + token.setExpires_in(openIDToken.getExpiresIn()); + token.setToken_type(openIDToken.getTokenType()); + + return token; + } } diff --git a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java b/api/common/endpoint/src/main/java/org/apache/servicecomb/authentication/token/TokenConfiguration.java similarity index 55% copy from samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java copy to api/common/endpoint/src/main/java/org/apache/servicecomb/authentication/token/TokenConfiguration.java index e69217f..94b1e21 100644 --- a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java +++ b/api/common/endpoint/src/main/java/org/apache/servicecomb/authentication/token/TokenConfiguration.java @@ -15,32 +15,30 @@ * limitations under the License. */ -package org.apache.servicecomb.authentication.resource; +package org.apache.servicecomb.authentication.token; -import org.apache.servicecomb.authentication.token.JWTTokenStore; -import org.apache.servicecomb.authentication.token.TokenStore; +import org.apache.servicecomb.authentication.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.jwt.crypto.sign.MacSigner; -import org.springframework.security.jwt.crypto.sign.SignatureVerifier; +import org.springframework.core.annotation.Order; import org.springframework.security.jwt.crypto.sign.Signer; import org.springframework.security.jwt.crypto.sign.SignerVerifier; @Configuration -public class AuthenticationConfiguration { - @Bean(name = {"authSigner", "authSignatureVerifier"}) - public SignerVerifier authSignerVerifier() { - // If using RSA, need to configure authSigner and authSignatureVerifier separately. - // If using MacSigner, need to protect the shared key by properly encryption. - return new MacSigner("Please change this key."); +public class TokenConfiguration { + @Bean(name = {Constants.BEAN_AUTH_ACCESS_TOKEN_STORE, + Constants.BEAN_AUTH_REFRESH_TOKEN_STORE}) + @Order(Constants.BEAN_DEFAULT_ORDER) + public SessionTokenStore sessionTokenStore() { + return new SessionTokenStore(); } - @Bean(name = "authIDTokenStore") - public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer, - @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) { - return new JWTTokenStore(signer, signerVerifier); + @Bean(name = {Constants.BEAN_AUTH_ID_TOKEN_STORE}) + @Order(Constants.BEAN_DEFAULT_ORDER) + public JWTTokenStore jwtTokenStore(@Autowired @Qualifier(Constants.BEAN_AUTH_SIGNER) Signer signer, + @Autowired @Qualifier(Constants.BEAN_AUTH_SIGNATURE_VERIFIER) SignerVerifier signerVerifier) { + return new JWTTokenStoreImpl(signer, signerVerifier); } - } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractOpenIDTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractOpenIDTokenStore.java new file mode 100644 index 0000000..9bc43cf --- /dev/null +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractOpenIDTokenStore.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.authentication.token; + +import org.apache.servicecomb.authentication.util.Constants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.userdetails.UserDetails; + +public abstract class AbstractOpenIDTokenStore implements OpenIDTokenStore { + @Autowired + @Qualifier(Constants.BEAN_AUTH_ACCESS_TOKEN_STORE) + private TokenStore<SessionToken> accessTokenStore; + + @Autowired + @Qualifier(Constants.BEAN_AUTH_REFRESH_TOKEN_STORE) + private TokenStore<SessionToken> refreshTokenStore; + + @Autowired + @Qualifier(Constants.BEAN_AUTH_ID_TOKEN_STORE) + private JWTTokenStore idTokenStore; + + @Override + public JWTToken createIDTokenByValue(String jwtTokenValue) { + return idTokenStore.createTokenByValue(jwtTokenValue); + } + + @Override + public OpenIDToken createToken(UserDetails userDetails) { + OpenIDToken token = new OpenIDToken(); + token.setTokenType(Constants.TOKEN_TYPE_BEARER); + token.setAccessToken(accessTokenStore.createToken(userDetails)); + token.setRefreshToken(refreshTokenStore.createToken(userDetails)); + token.setIdToken(idTokenStore.createToken(userDetails)); + return token; + } +} diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemoryOpenIDTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemoryOpenIDTokenStore.java new file mode 100644 index 0000000..522e475 --- /dev/null +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemoryOpenIDTokenStore.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.authentication.token; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class InMemoryOpenIDTokenStore extends AbstractOpenIDTokenStore { + private static final Map<String, OpenIDToken> TOKENS = new ConcurrentHashMap<>(); + + private static final Map<String, OpenIDToken> TOKENS_BY_REFRESH_TOKEN_VALUE = new ConcurrentHashMap<>(); + + private static final Map<String, OpenIDToken> TOKENS_BY_ID_TOKEN_VALUE = new ConcurrentHashMap<>(); + + @Override + public OpenIDToken readTokenByValue(String value) { + return TOKENS.get(value); + } + + @Override + public OpenIDToken readTokenByRefreshTokenValue(String refreshTokenValue) { + return TOKENS_BY_REFRESH_TOKEN_VALUE.get(refreshTokenValue); + } + + @Override + public OpenIDToken readTokenByIDTokenValue(String idTokenValue) { + return TOKENS_BY_ID_TOKEN_VALUE.get(idTokenValue); + } + + @Override + public void saveToken(OpenIDToken token) { + TOKENS.put(token.getValue(), token); + TOKENS_BY_REFRESH_TOKEN_VALUE.put(token.getRefreshToken().getValue(), token); + TOKENS_BY_ID_TOKEN_VALUE.put(token.getIdToken().getValue(), token); + } +} diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemorySessionIDTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemorySessionIDTokenStore.java deleted file mode 100644 index 5bac8f0..0000000 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/InMemorySessionIDTokenStore.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.authentication.token; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.security.core.userdetails.UserDetails; - -public class InMemorySessionIDTokenStore extends AbstractSessionIDTokenStore { - private Map<String, SessionIDToken> tokens = new HashMap<>(); - - @SuppressWarnings("unchecked") - @Override - public <T extends Token> T createToken(UserDetails userDetails) { - SessionIDToken token = new SessionIDToken(userDetails.getUsername()); - tokens.put(token.getValue(), token); - return (T) token; - } - - @SuppressWarnings("unchecked") - @Override - public <T extends Token> T readTokenByValue(String value) { - return (T) tokens.get(value); - } - -} diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java index e989ae5..a4c6750 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java @@ -17,69 +17,8 @@ package org.apache.servicecomb.authentication.token; -import java.util.Map; - import org.apache.servicecomb.authentication.jwt.JWTClaims; -import org.apache.servicecomb.authentication.jwt.JsonParser; -import org.springframework.security.jwt.Jwt; -import org.springframework.security.jwt.JwtHelper; -import org.springframework.security.jwt.crypto.sign.Signer; - -public class JWTToken implements Token { - private JWTClaims claims; - - private boolean valueCalculated = false; - - private String value; - - private Signer signer; - - public JWTToken(JWTClaims claims, Signer signer) { - this.claims = claims; - this.signer = signer; - } - - @Override - public boolean isExpired() { - return System.currentTimeMillis() - this.getIssueAt() > this.getExpiration() * 1000; - } - - @Override - public long getIssueAt() { - return this.claims.getIat(); - } - - @Override - public long getExpiration() { - return this.claims.getExp(); - } - - @Override - public long getNotBefore() { - return this.claims.getNbf(); - } - - @Override - public String getValue() { - if (!this.valueCalculated) { - String content = JsonParser.unparse(claims); - Jwt jwtToken = JwtHelper.encode(content, signer); - this.value = jwtToken.getEncoded(); - } - return this.value; - } - - @Override - public Map<String, Object> getAdditionalInformation() { - return this.claims.getAdditionalInformation(); - } - @Override - public String username() { - return this.claims.getSub(); - } - - public JWTClaims getClaims() { - return this.claims; - } +public interface JWTToken extends Token { + public JWTClaims getClaims(); } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenImpl.java similarity index 93% copy from api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenImpl.java index e989ae5..0a99334 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTToken.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenImpl.java @@ -25,7 +25,7 @@ import org.springframework.security.jwt.Jwt; import org.springframework.security.jwt.JwtHelper; import org.springframework.security.jwt.crypto.sign.Signer; -public class JWTToken implements Token { +public class JWTTokenImpl implements JWTToken { private JWTClaims claims; private boolean valueCalculated = false; @@ -34,14 +34,14 @@ public class JWTToken implements Token { private Signer signer; - public JWTToken(JWTClaims claims, Signer signer) { + public JWTTokenImpl(JWTClaims claims, Signer signer) { this.claims = claims; this.signer = signer; } @Override public boolean isExpired() { - return System.currentTimeMillis() - this.getIssueAt() > this.getExpiration() * 1000; + return System.currentTimeMillis() - this.getIssueAt() > this.getExpiresIn() * 1000; } @Override @@ -50,7 +50,7 @@ public class JWTToken implements Token { } @Override - public long getExpiration() { + public long getExpiresIn() { return this.claims.getExp(); } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java index 2f394e7..67e51cc 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java @@ -17,55 +17,6 @@ package org.apache.servicecomb.authentication.token; -import java.util.UUID; - -import org.apache.servicecomb.authentication.jwt.JWTClaims; -import org.apache.servicecomb.authentication.jwt.JsonParser; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.jwt.Jwt; -import org.springframework.security.jwt.JwtHelper; -import org.springframework.security.jwt.crypto.sign.SignatureVerifier; -import org.springframework.security.jwt.crypto.sign.Signer; - -public class JWTTokenStore implements TokenStore { - private Signer signer; - - private SignatureVerifier signerVerifier; - - public JWTTokenStore(Signer signer, SignatureVerifier signerVerifier) { - this.signer = signer; - this.signerVerifier = signerVerifier; - } - - @SuppressWarnings("unchecked") - @Override - public <T extends Token> T createToken(UserDetails userDetails) { - JWTClaims claims = new JWTClaims(); - claims.setSub(userDetails.getUsername()); - if (userDetails.getAuthorities() != null) { - userDetails.getAuthorities().forEach(authority -> claims.addAuthority(authority.getAuthority())); - } - - // TODO : set other parameters. - claims.setJti(UUID.randomUUID().toString()); - claims.setIat(System.currentTimeMillis()); - claims.setExp(5 * 60); - - return (T) new JWTToken(claims, signer); - } - - @SuppressWarnings("unchecked") - @Override - public <T extends Token> T readTokenByValue(String value) { - Jwt jwt = JwtHelper.decode(value); - JWTClaims claims; - try { - jwt.verifySignature(signerVerifier); - claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class); - } catch (Exception e) { - return null; - } - return (T) new JWTToken(claims, signer); - } - +public interface JWTTokenStore extends TokenStore<JWTToken> { + public JWTToken createTokenByValue(String value); } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStoreImpl.java similarity index 78% copy from api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStoreImpl.java index 2f394e7..f7191c0 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStore.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/JWTTokenStoreImpl.java @@ -27,19 +27,18 @@ import org.springframework.security.jwt.JwtHelper; import org.springframework.security.jwt.crypto.sign.SignatureVerifier; import org.springframework.security.jwt.crypto.sign.Signer; -public class JWTTokenStore implements TokenStore { +public class JWTTokenStoreImpl implements JWTTokenStore { private Signer signer; - private SignatureVerifier signerVerifier; + private SignatureVerifier signatureVerifier; - public JWTTokenStore(Signer signer, SignatureVerifier signerVerifier) { + public JWTTokenStoreImpl(Signer signer, SignatureVerifier signatureVerifier) { this.signer = signer; - this.signerVerifier = signerVerifier; + this.signatureVerifier = signatureVerifier; } - @SuppressWarnings("unchecked") @Override - public <T extends Token> T createToken(UserDetails userDetails) { + public JWTToken createToken(UserDetails userDetails) { JWTClaims claims = new JWTClaims(); claims.setSub(userDetails.getUsername()); if (userDetails.getAuthorities() != null) { @@ -51,21 +50,18 @@ public class JWTTokenStore implements TokenStore { claims.setIat(System.currentTimeMillis()); claims.setExp(5 * 60); - return (T) new JWTToken(claims, signer); + return new JWTTokenImpl(claims, signer); } - @SuppressWarnings("unchecked") - @Override - public <T extends Token> T readTokenByValue(String value) { + public JWTToken createTokenByValue(String value) { Jwt jwt = JwtHelper.decode(value); JWTClaims claims; try { - jwt.verifySignature(signerVerifier); + jwt.verifySignature(signatureVerifier); claims = JsonParser.parse(jwt.getClaims(), JWTClaims.class); } catch (Exception e) { return null; } - return (T) new JWTToken(claims, signer); + return new JWTTokenImpl(claims, signer); } - } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/OpenIDToken.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/OpenIDToken.java new file mode 100644 index 0000000..2587619 --- /dev/null +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/OpenIDToken.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.authentication.token; + +import java.util.Map; +import java.util.Set; + +public class OpenIDToken implements Token { + private String tokenType; + + private SessionToken accessToken; + + private SessionToken refreshToken; + + private JWTToken idToken; + + private Set<String> scope; + + public String getTokenType() { + return tokenType; + } + + public void setTokenType(String tokenType) { + this.tokenType = tokenType; + } + + public SessionToken getAccessToken() { + return accessToken; + } + + public void setAccessToken(SessionToken accessToken) { + this.accessToken = accessToken; + } + + public SessionToken getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(SessionToken refreshToken) { + this.refreshToken = refreshToken; + } + + public JWTToken getIdToken() { + return idToken; + } + + public void setIdToken(JWTToken idToken) { + this.idToken = idToken; + } + + public Set<String> getScope() { + return scope; + } + + public void setScope(Set<String> scope) { + this.scope = scope; + } + + @Override + public String username() { + return accessToken.username(); + } + + @Override + public boolean isExpired() { + return accessToken.isExpired(); + } + + @Override + public long getIssueAt() { + return accessToken.getIssueAt(); + } + + @Override + public long getExpiresIn() { + return accessToken.getExpiresIn(); + } + + @Override + public long getNotBefore() { + return accessToken.getNotBefore(); + } + + @Override + public String getValue() { + return accessToken.getValue(); + } + + @Override + public Map<String, Object> getAdditionalInformation() { + return accessToken.getAdditionalInformation(); + } +} diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/OpenIDTokenStore.java similarity index 71% copy from api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/OpenIDTokenStore.java index 21b06d5..cd65ead 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/OpenIDTokenStore.java @@ -17,20 +17,15 @@ package org.apache.servicecomb.authentication.token; -import java.util.Map; +public interface OpenIDTokenStore extends TokenStore<OpenIDToken> { -public interface Token { - String username(); + OpenIDToken readTokenByValue(String value); - boolean isExpired(); + OpenIDToken readTokenByRefreshTokenValue(String refreshTokenValue); - long getIssueAt(); + OpenIDToken readTokenByIDTokenValue(String idTokenValue); + + JWTToken createIDTokenByValue(String jwtTokenValue); - long getExpiration(); - - long getNotBefore(); - - String getValue(); - - Map<String, Object> getAdditionalInformation(); + void saveToken(OpenIDToken token); } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractSessionIDTokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionToken.java similarity index 92% rename from api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractSessionIDTokenStore.java rename to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionToken.java index cb507a3..c39cec4 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/AbstractSessionIDTokenStore.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionToken.java @@ -17,5 +17,5 @@ package org.apache.servicecomb.authentication.token; -public abstract class AbstractSessionIDTokenStore implements TokenStore { +public interface SessionToken extends Token { } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionIDToken.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionTokenImpl.java similarity index 87% rename from api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionIDToken.java rename to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionTokenImpl.java index 06a6f96..53aa6d0 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionIDToken.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionTokenImpl.java @@ -20,27 +20,27 @@ package org.apache.servicecomb.authentication.token; import java.util.Map; import java.util.UUID; -public class SessionIDToken implements Token { +public class SessionTokenImpl implements SessionToken { private String value; private long issueAt; // in seconds - private long expiration; + private long expiresIn; private String username; - public SessionIDToken(String username) { + public SessionTokenImpl(String username) { this.value = UUID.randomUUID().toString(); this.issueAt = System.currentTimeMillis(); // TODO add a configuration - this.expiration = 600; + this.expiresIn = 600; this.username = username; } @Override public boolean isExpired() { - return System.currentTimeMillis() - this.issueAt > this.expiration * 1000; + return System.currentTimeMillis() - this.issueAt > this.expiresIn * 1000; } @Override @@ -49,8 +49,8 @@ public class SessionIDToken implements Token { } @Override - public long getExpiration() { - return this.expiration; + public long getExpiresIn() { + return this.expiresIn; } @Override @@ -70,6 +70,7 @@ public class SessionIDToken implements Token { return null; } + @Override public String username() { return this.username; } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionTokenStore.java similarity index 81% copy from api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java copy to api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionTokenStore.java index 1f26610..59b23aa 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/SessionTokenStore.java @@ -19,8 +19,11 @@ package org.apache.servicecomb.authentication.token; import org.springframework.security.core.userdetails.UserDetails; -public interface TokenStore { - <T extends Token> T createToken(UserDetails userDetails); - - <T extends Token> T readTokenByValue(String value); +public class SessionTokenStore implements TokenStore<SessionToken> { + + @Override + public SessionToken createToken(UserDetails userDetails) { + return new SessionTokenImpl(userDetails.getUsername()); + } + } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java index 21b06d5..29b26db 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/Token.java @@ -26,7 +26,7 @@ public interface Token { long getIssueAt(); - long getExpiration(); + long getExpiresIn(); long getNotBefore(); diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java index 1f26610..e4b6df1 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/token/TokenStore.java @@ -19,8 +19,6 @@ package org.apache.servicecomb.authentication.token; import org.springframework.security.core.userdetails.UserDetails; -public interface TokenStore { - <T extends Token> T createToken(UserDetails userDetails); - - <T extends Token> T readTokenByValue(String value); +public interface TokenStore<T extends Token> { + T createToken(UserDetails userDetails); } diff --git a/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java b/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java index 4dcc8e7..43c85b5 100644 --- a/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java +++ b/api/common/service/src/main/java/org/apache/servicecomb/authentication/util/Constants.java @@ -22,8 +22,35 @@ public final class Constants { public static final String CONTEXT_HEADER_AUTHORIZATION = "Authorization"; + public static final String CONTEXT_HEADER_AUTHORIZATION_TYPE = "Authorization-TYPE"; + + public static final String CONTEXT_HEADER_AUTHORIZATION_TYPE_ID_TOKEN = "ID_TOKEN"; + + public static final String CONTEXT_HEADER_AUTHORIZATION_TYPE_SESSION_TOKEN = "SESSION_TOKEN"; + public static final String CONTEXT_HEADER_CLAIMS = "Claims"; public static final String TOKEN_TYPE_BEARER = "Bearer"; + public static final int BEAN_DEFAULT_ORDER = 100; + + public static final String BEAN_AUTH_EDGE_TOKEN_RESPONSE_PROCESSOR = "edgeTokenResponseProcessor"; + + public static final String BEAN_AUTH_SIGNER = "authSigner"; + + public static final String BEAN_AUTH_SIGNATURE_VERIFIER = "authSignatureVerifier"; + + public static final String BEAN_AUTH_PASSWORD_ENCODER = "authPasswordEncoder"; + + public static final String BEAN_AUTH_ACCESS_TOKEN_STORE = "authAccessTokenStore"; + + public static final String BEAN_AUTH_REFRESH_TOKEN_STORE = "authRefreshTokenStore"; + + public static final String BEAN_AUTH_ID_TOKEN_STORE = "authIDTokenStore"; + + public static final String BEAN_AUTH_OPEN_ID_TOKEN_STORE = "authOpenIDTokenStore"; + + public static final String BEAN_AUTH_USER_DETAILS_SERVICE = "authUserDetailsService"; + + public static final String CONFIG_GRANTER_PASSWORD_ENABLED = "servicecomb.authentication.granter.password.enabled"; } diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java index d209be4..850b733 100644 --- a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java +++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthHandler.java @@ -17,7 +17,10 @@ package org.apache.servicecomb.authentication.edge; -import org.apache.servicecomb.authentication.server.TokenResponse; +import org.apache.servicecomb.authentication.token.JWTToken; +import org.apache.servicecomb.authentication.token.JWTTokenStore; +import org.apache.servicecomb.authentication.token.OpenIDToken; +import org.apache.servicecomb.authentication.token.OpenIDTokenStore; import org.apache.servicecomb.authentication.util.Constants; import org.apache.servicecomb.core.Handler; import org.apache.servicecomb.core.Invocation; @@ -25,27 +28,43 @@ import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; - public class AuthHandler implements Handler { @Override public void handle(Invocation invocation, AsyncResponse asyncResponse) throws Exception { - EdgeTokenStore edgeTokenStore = BeanUtils.getBean("authEdgeTokenStore"); - String token = invocation.getContext(Constants.CONTEXT_HEADER_AUTHORIZATION); + String tokenType = invocation.getContext(Constants.CONTEXT_HEADER_AUTHORIZATION_TYPE); if (token == null) { asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); return; } - TokenResponse tokenResonse = edgeTokenStore.readTokenResponse(token); - if (tokenResonse == null) { - // TODO : check token validity by expiration time + if (Constants.CONTEXT_HEADER_AUTHORIZATION_TYPE_ID_TOKEN.equals(tokenType)) { + JWTTokenStore jwtTokenStore = BeanUtils.getBean(Constants.BEAN_AUTH_ID_TOKEN_STORE); + JWTToken jwtToken = jwtTokenStore.createTokenByValue(token); + if (jwtToken == null || jwtToken.isExpired()) { + asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); + return; + } + + // send id_token to services to apply state less validation + invocation.addContext(Constants.CONTEXT_HEADER_AUTHORIZATION, jwtToken.getValue()); + invocation.next(asyncResponse); + } else if (Constants.CONTEXT_HEADER_AUTHORIZATION_TYPE_SESSION_TOKEN.equals(tokenType)) { + OpenIDTokenStore openIDTokenStore = BeanUtils.getBean(Constants.BEAN_AUTH_OPEN_ID_TOKEN_STORE); + + + OpenIDToken tokenResonse = openIDTokenStore.readTokenByValue(token); + if (tokenResonse == null || tokenResonse.isExpired()) { + asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); + return; + } + + // send id_token to services to apply state less validation + invocation.addContext(Constants.CONTEXT_HEADER_AUTHORIZATION, tokenResonse.getIdToken().getValue()); + invocation.next(asyncResponse); + } else { asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); return; } - - // send id_token to services to apply state less validation - invocation.addContext(Constants.CONTEXT_HEADER_AUTHORIZATION, tokenResonse.getId_token()); - invocation.next(asyncResponse); } } diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java index 46ba288..6b5b8d7 100644 --- a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java +++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/AuthenticationFilter.java @@ -32,12 +32,16 @@ public class AuthenticationFilter implements HttpServerFilter { @Override public Response afterReceiveRequest(Invocation invocation, HttpServletRequestEx requestEx) { + // Now support bearer id tokens authentication + // TODO : add support for Cookies session tokens. String authentication = requestEx.getHeader(Constants.HTTP_HEADER_AUTHORIZATION); if (authentication != null) { String[] tokens = authentication.split(" "); if (tokens.length == 2) { - if (tokens[0].equals("Bearer")) { + if (tokens[0].equals(Constants.TOKEN_TYPE_BEARER)) { invocation.addContext(Constants.CONTEXT_HEADER_AUTHORIZATION, tokens[1]); + invocation.addContext(Constants.CONTEXT_HEADER_AUTHORIZATION_TYPE, + Constants.CONTEXT_HEADER_AUTHORIZATION_TYPE_ID_TOKEN); } } } diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/DumyEdgeTokenResponseProcessor.java similarity index 82% copy from api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java copy to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/DumyEdgeTokenResponseProcessor.java index 92bbbaf..97dd3c4 100644 --- a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java +++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/DumyEdgeTokenResponseProcessor.java @@ -19,8 +19,12 @@ package org.apache.servicecomb.authentication.edge; import org.apache.servicecomb.authentication.server.TokenResponse; -public interface EdgeTokenStore { - TokenResponse readTokenResponse(String accessTokenValue); +public class DumyEdgeTokenResponseProcessor implements EdgeTokenResponseProcessor { + public DumyEdgeTokenResponseProcessor() { + } + + @Override + public void process(TokenResponse tokenResponse) { + } - void saveTokenResponse(String accessTokenValue, TokenResponse tokenResponse); } diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InMemoryEdgeTokenStore.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeConfiguration.java similarity index 62% rename from api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InMemoryEdgeTokenStore.java rename to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeConfiguration.java index 03a7baa..502dc7e 100644 --- a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/InMemoryEdgeTokenStore.java +++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeConfiguration.java @@ -17,22 +17,16 @@ package org.apache.servicecomb.authentication.edge; -import java.util.HashMap; -import java.util.Map; +import org.apache.servicecomb.authentication.util.Constants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; -import org.apache.servicecomb.authentication.server.TokenResponse; - -public class InMemoryEdgeTokenStore implements EdgeTokenStore { - private Map<String, TokenResponse> tokens = new HashMap<>(); - - @Override - public TokenResponse readTokenResponse(String accessTokenValue) { - return tokens.get(accessTokenValue); +@Configuration +public class EdgeConfiguration { + @Bean(name = {Constants.BEAN_AUTH_EDGE_TOKEN_RESPONSE_PROCESSOR}) + @Order(Constants.BEAN_DEFAULT_ORDER) + public EdgeTokenResponseProcessor edgeTokenResponseProcessor() { + return new DumyEdgeTokenResponseProcessor(); } - - @Override - public void saveTokenResponse(String accessTokenValue, TokenResponse tokenResponse) { - tokens.put(accessTokenValue, tokenResponse); - } - } diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenResponseProcessor.java similarity index 84% rename from api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java rename to api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenResponseProcessor.java index 92bbbaf..09ea7b3 100644 --- a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenStore.java +++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/EdgeTokenResponseProcessor.java @@ -19,8 +19,6 @@ package org.apache.servicecomb.authentication.edge; import org.apache.servicecomb.authentication.server.TokenResponse; -public interface EdgeTokenStore { - TokenResponse readTokenResponse(String accessTokenValue); - - void saveTokenResponse(String accessTokenValue, TokenResponse tokenResponse); +public interface EdgeTokenResponseProcessor { + void process(TokenResponse tokenResponse); } diff --git a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java index 87dcbb0..87f7696 100644 --- a/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java +++ b/api/edge-service/endpoint/src/main/java/org/apache/servicecomb/authentication/edge/TokenEndpoint.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.authentication.server.TokenResponse; +import org.apache.servicecomb.authentication.util.Constants; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; @@ -38,9 +39,8 @@ public class TokenEndpoint implements TokenService { private AuthenticationServerTokenEndpoint authenticationSererTokenEndpoint; @Autowired - @Qualifier("authEdgeTokenStore") - private EdgeTokenStore edgeTokenStore; - + @Qualifier(Constants.BEAN_AUTH_EDGE_TOKEN_RESPONSE_PROCESSOR) + private EdgeTokenResponseProcessor edgeTokenResponseProcessor; @Override @PostMapping(path = "/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) @@ -55,7 +55,7 @@ public class TokenEndpoint implements TokenService { response.whenComplete((tokenResonse, ex) -> { if (!response.isCompletedExceptionally()) { result.complete(tokenResonse); - edgeTokenStore.saveTokenResponse(tokenResonse.getAccess_token(), tokenResonse); + edgeTokenResponseProcessor.process(tokenResonse); } else { result.completeExceptionally(ex); } diff --git a/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java b/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java index 5c81139..73f0b1a 100644 --- a/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java +++ b/api/resource-server/service/src/main/java/org/apache/servicecomb/authentication/resource/ResourceAuthHandler.java @@ -48,25 +48,25 @@ public class ResourceAuthHandler implements Handler { return; } - String token = invocation.getContext(Constants.CONTEXT_HEADER_AUTHORIZATION); - if (token == null) { + String idTokenValue = invocation.getContext(Constants.CONTEXT_HEADER_AUTHORIZATION); + if (idTokenValue == null) { asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); return; } // verify tokens - JWTTokenStore store = BeanUtils.getBean("authIDTokenStore"); - JWTToken t = store.readTokenByValue(token); - if(t == null) { + JWTTokenStore store = BeanUtils.getBean(Constants.BEAN_AUTH_ID_TOKEN_STORE); + JWTToken idToken = store.createTokenByValue(idTokenValue); + if (idToken == null) { asyncResponse.consumerFail(new InvocationException(403, "forbidden", "not authenticated")); return; } - + // check roles if (!StringUtils.isEmpty(config.roles)) { String[] roles = config.roles.split(","); if (roles.length > 0) { boolean valid = false; - Set<String> authorities = t.getClaims().getAuthorities(); + Set<String> authorities = idToken.getClaims().getAuthorities(); for (String role : roles) { if (authorities.contains(role)) { valid = true; @@ -81,8 +81,8 @@ public class ResourceAuthHandler implements Handler { } // pre method authentiation - Set<GrantedAuthority> grantedAuthorities = new HashSet<>(t.getClaims().getAuthorities().size()); - t.getClaims().getAuthorities().forEach(v -> grantedAuthorities.add(new SimpleGrantedAuthority(v))); + Set<GrantedAuthority> grantedAuthorities = new HashSet<>(idToken.getClaims().getAuthorities().size()); + idToken.getClaims().getAuthorities().forEach(v -> grantedAuthorities.add(new SimpleGrantedAuthority(v))); SecurityContext sc = new SecurityContextImpl(); Authentication authentication = new SimpleAuthentication(true, grantedAuthorities); sc.setAuthentication(authentication); diff --git a/docs/zh_CN/developersGuide.md b/docs/zh_CN/developersGuide.md index 3d67833..a681c5a 100644 --- a/docs/zh_CN/developersGuide.md +++ b/docs/zh_CN/developersGuide.md @@ -22,16 +22,7 @@ grant_type=password&username=admin&password=changeMyPassword "token_type": "bearer", "access_token": "SlAV32hkKG", "refresh_token": "8xLOxBtZp8", - "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc - yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5 - NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ - fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz - AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q - Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ - NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd - QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS - K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4 - XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg", + "id_token": "eyJ...hbGciOiJSU...zI1NiIsImtpZCI6Ij", "expires_in": 600, "scope": null, "jti": null, @@ -48,7 +39,7 @@ grant_type=password&username=admin&password=changeMyPassword POST http://localhost:9090/api/resource-server/v1/auth/handler/adminSayHello?name=Hi HTTP/1.1 Content-Type: application/x-www-form-urlencoded -Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW +Authorization: Bearer SlAV32hkKG ``` * Edge Service 将 Access Token 转换为对应的 ID Token , 然后将请求转发给Resource Server。 diff --git a/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java b/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java index 20e9f68..49639ff 100644 --- a/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java +++ b/samples/AuthenticationServer/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java @@ -19,9 +19,9 @@ package org.apache.servicecomb.authentication; import java.util.Arrays; -import org.apache.servicecomb.authentication.token.InMemorySessionIDTokenStore; -import org.apache.servicecomb.authentication.token.JWTTokenStore; -import org.apache.servicecomb.authentication.token.TokenStore; +import org.apache.servicecomb.authentication.token.AbstractOpenIDTokenStore; +import org.apache.servicecomb.authentication.token.InMemoryOpenIDTokenStore; +import org.apache.servicecomb.authentication.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; @@ -33,41 +33,33 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; import org.springframework.security.jwt.crypto.sign.MacSigner; -import org.springframework.security.jwt.crypto.sign.SignatureVerifier; -import org.springframework.security.jwt.crypto.sign.Signer; import org.springframework.security.jwt.crypto.sign.SignerVerifier; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @Configuration public class AuthenticationConfiguration { - @Bean(name = "authPasswordEncoder") + @Bean(name = Constants.BEAN_AUTH_PASSWORD_ENCODER) public PasswordEncoder authPasswordEncoder() { return new Pbkdf2PasswordEncoder(); } - @Bean(name = {"authSigner", "authSignatureVerifier"}) + @Bean(name = {Constants.BEAN_AUTH_SIGNER, Constants.BEAN_AUTH_SIGNATURE_VERIFIER}) public SignerVerifier authSignerVerifier() { // If using RSA, need to configure authSigner and authSignatureVerifier separately. // If using MacSigner, need to protect the shared key by properly encryption. return new MacSigner("Please change this key."); } - @Bean(name = {"authAccessTokenStore", "authRefreshTokenStore"}) - public TokenStore sessionIDTokenStore() { - // Use in memory store for testing. Need to implement JDBC or Redis SessionIDTokenStore in product. - return new InMemorySessionIDTokenStore(); + @Bean(name = Constants.BEAN_AUTH_OPEN_ID_TOKEN_STORE) + public AbstractOpenIDTokenStore openIDTokenStore() { + // TODO: Use in memory store for testing. Need to implement JDBC or Redis SessionIDTokenStore in product. + return new InMemoryOpenIDTokenStore(); } - @Bean(name = "authIDTokenStore") - public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer, - @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) { - return new JWTTokenStore(signer, signerVerifier); - } - - @Bean(name = "authUserDetailsService") + @Bean(name = Constants.BEAN_AUTH_USER_DETAILS_SERVICE) public UserDetailsService authUserDetailsService( - @Autowired @Qualifier("authPasswordEncoder") PasswordEncoder passwordEncoder) { - // Use in memory UserDetails, need to implement JDBC or others in product + @Autowired @Qualifier(Constants.BEAN_AUTH_PASSWORD_ENCODER) PasswordEncoder passwordEncoder) { + // TODO: Use in memory UserDetails, need to implement JDBC or others in product InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); UserDetails uAdmin = new User("admin", passwordEncoder.encode("changeMyPassword"), Arrays.asList(new SimpleGrantedAuthority("ADMIN"))); diff --git a/samples/Client/pom.xml b/samples/Client/pom.xml index d879756..5bbe9d8 100644 --- a/samples/Client/pom.xml +++ b/samples/Client/pom.xml @@ -33,11 +33,6 @@ <dependencies> <dependency> <groupId>org.apache.servicecomb.authentication</groupId> - <artifactId>authentication-common-api-endpoint</artifactId> - <version>0.0.1-SNAPSHOT</version> - </dependency> - <dependency> - <groupId>org.apache.servicecomb.authentication</groupId> <artifactId>authentication-server-api-service</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> @@ -69,10 +64,6 @@ <dependencies> <dependency> <groupId>org.apache.servicecomb.authentication</groupId> - <artifactId>authentication-common-api-endpoint</artifactId> - </dependency> - <dependency> - <groupId>org.apache.servicecomb.authentication</groupId> <artifactId>authentication-server-api-service</artifactId> </dependency> <dependency> diff --git a/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java b/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java index e05545f..eb13bf1 100644 --- a/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java +++ b/samples/Client/src/main/java/org/apache/servicecomb/authentication/AuthenticationTestCase.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.authentication; import org.apache.servicecomb.authentication.server.TokenResponse; +import org.apache.servicecomb.authentication.util.Constants; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -30,16 +31,16 @@ import org.springframework.web.client.HttpClientErrorException; public class AuthenticationTestCase implements TestCase { @Override public void run() { - String accessToken = accessToken(); - testHanlderAuth(accessToken); - testMethodAuth(accessToken); + String idToken = idToken(); + testHanlderAuth(idToken); + testMethodAuth(idToken); - accessToken = accessTokenByRefreshToken(); - testHanlderAuth(accessToken); - testMethodAuth(accessToken); + idToken = idTokenByRefreshToken(); + testHanlderAuth(idToken); + testMethodAuth(idToken); } - private String accessToken() { + private String idToken() { // get token MultiValueMap<String, Object> map = new LinkedMultiValueMap<>(); map.add("grant_type", "password"); @@ -52,12 +53,12 @@ public class AuthenticationTestCase implements TestCase { BootEventListener.edgeServiceTokenEndpoint.postForObject("/", new HttpEntity<>(map, headers), TokenResponse.class); - TestMgr.check("bearer", token.getToken_type()); - TestMgr.check(true, token.getAccess_token().length() > 10); - return token.getAccess_token(); + TestMgr.check(Constants.TOKEN_TYPE_BEARER, token.getToken_type()); + TestMgr.check(true, token.getId_token().length() > 10); + return token.getId_token(); } - private String accessTokenByRefreshToken() { + private String idTokenByRefreshToken() { // get token MultiValueMap<String, Object> map = new LinkedMultiValueMap<>(); map.add("grant_type", "password"); @@ -70,7 +71,7 @@ public class AuthenticationTestCase implements TestCase { BootEventListener.edgeServiceTokenEndpoint.postForObject("/", new HttpEntity<>(map, headers), TokenResponse.class); - TestMgr.check("bearer", token.getToken_type()); + TestMgr.check(Constants.TOKEN_TYPE_BEARER, token.getToken_type()); TestMgr.check(true, token.getAccess_token().length() > 10); // refresh token @@ -83,11 +84,11 @@ public class AuthenticationTestCase implements TestCase { new HttpEntity<>(map, headers), TokenResponse.class); TestMgr.check(token.getToken_type(), tokenNew.getToken_type()); - TestMgr.check(token.getRefresh_token(), tokenNew.getRefresh_token()); + TestMgr.check(token.getRefresh_token().equals(tokenNew.getRefresh_token()), false); TestMgr.check(token.getAccess_token().equals(tokenNew.getAccess_token()), false); TestMgr.check(token.getId_token().equals(tokenNew.getId_token()), false); - return tokenNew.getAccess_token(); + return tokenNew.getId_token(); } private void testHanlderAuth(String accessToken) { diff --git a/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestEndpoint.java b/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestEndpoint.java index 19b0af1..a89f8c2 100644 --- a/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestEndpoint.java +++ b/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestEndpoint.java @@ -32,19 +32,25 @@ public class TestEndpoint { @GetMapping(path = "/start") public String start() { - tests.forEach(test -> test.run()); + tests.forEach(test -> { + try { + test.run(); + } catch (Throwable e) { + TestMgr.failed(e.getMessage(), e); + } + }); + + TestMgr.summary(); List<Throwable> errors = TestMgr.errors(); if (TestMgr.isSuccess()) { return TestMgr.successMessage(); } else { - TestMgr.summary(); - StringBuilder sb = new StringBuilder(); sb.append("Failed count : " + errors.size()); sb.append("\n"); errors.forEach(t -> sb.append(t.getMessage() + "\n")); - + TestMgr.reset(); return sb.toString(); } diff --git a/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestMgr.java b/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestMgr.java index 030bb4c..c9e71fe 100644 --- a/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestMgr.java +++ b/samples/Client/src/main/java/org/apache/servicecomb/authentication/TestMgr.java @@ -71,9 +71,9 @@ public class TestMgr { } public static void failed(String desc, Throwable e) { - Error error = new Error(msg + " | " + desc + ", method is " + getCaller()); + Error error = new Error(msg + " | " + desc + ", method is " + getCaller() + " , error type is " + e.getClass().toString()); if (e != null) { - error.setStackTrace(error.getStackTrace()); + error.setStackTrace(e.getStackTrace()); } errorList.add(error); } diff --git a/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java b/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java index e9187b7..62ab060 100644 --- a/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java +++ b/samples/EdgeService/src/main/java/org/apache/servicecomb/authentication/gateway/AuthenticationConfiguration.java @@ -17,15 +17,30 @@ package org.apache.servicecomb.authentication.gateway; -import org.apache.servicecomb.authentication.edge.EdgeTokenStore; -import org.apache.servicecomb.authentication.edge.InMemoryEdgeTokenStore; +import org.apache.servicecomb.authentication.token.JWTTokenStore; +import org.apache.servicecomb.authentication.token.JWTTokenStoreImpl; +import org.apache.servicecomb.authentication.util.Constants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.jwt.crypto.sign.MacSigner; +import org.springframework.security.jwt.crypto.sign.Signer; +import org.springframework.security.jwt.crypto.sign.SignerVerifier; @Configuration public class AuthenticationConfiguration { - @Bean(name = "authEdgeTokenStore") - public EdgeTokenStore authEdgeTokenStore() { - return new InMemoryEdgeTokenStore(); + @Bean(name = {Constants.BEAN_AUTH_SIGNER, Constants.BEAN_AUTH_SIGNATURE_VERIFIER}) + public SignerVerifier authSignerVerifier() { + // If using RSA, need to configure authSigner and authSignatureVerifier separately. + // If using MacSigner, need to protect the shared key by properly encryption. + return new MacSigner("Please change this key."); } + + @Bean(name = Constants.BEAN_AUTH_ID_TOKEN_STORE) + public JWTTokenStore authIDTokenStore(@Autowired @Qualifier(Constants.BEAN_AUTH_SIGNER) Signer signer, + @Autowired @Qualifier(Constants.BEAN_AUTH_SIGNATURE_VERIFIER) SignerVerifier signerVerifier) { + return new JWTTokenStoreImpl(signer, signerVerifier); + } + } diff --git a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java b/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java index e69217f..b1dcb00 100644 --- a/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java +++ b/samples/ResourceServer/src/main/java/org/apache/servicecomb/authentication/resource/AuthenticationConfiguration.java @@ -18,29 +18,29 @@ package org.apache.servicecomb.authentication.resource; import org.apache.servicecomb.authentication.token.JWTTokenStore; -import org.apache.servicecomb.authentication.token.TokenStore; +import org.apache.servicecomb.authentication.token.JWTTokenStoreImpl; +import org.apache.servicecomb.authentication.util.Constants; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.jwt.crypto.sign.MacSigner; -import org.springframework.security.jwt.crypto.sign.SignatureVerifier; import org.springframework.security.jwt.crypto.sign.Signer; import org.springframework.security.jwt.crypto.sign.SignerVerifier; @Configuration public class AuthenticationConfiguration { - @Bean(name = {"authSigner", "authSignatureVerifier"}) + @Bean(name = {Constants.BEAN_AUTH_SIGNER, Constants.BEAN_AUTH_SIGNATURE_VERIFIER}) public SignerVerifier authSignerVerifier() { // If using RSA, need to configure authSigner and authSignatureVerifier separately. // If using MacSigner, need to protect the shared key by properly encryption. return new MacSigner("Please change this key."); } - @Bean(name = "authIDTokenStore") - public TokenStore authIDTokenStore(@Autowired @Qualifier("authSigner") Signer signer, - @Autowired @Qualifier("authSignatureVerifier") SignatureVerifier signerVerifier) { - return new JWTTokenStore(signer, signerVerifier); + @Bean(name = Constants.BEAN_AUTH_ID_TOKEN_STORE) + public JWTTokenStore authIDTokenStore(@Autowired @Qualifier(Constants.BEAN_AUTH_SIGNER) Signer signer, + @Autowired @Qualifier(Constants.BEAN_AUTH_SIGNATURE_VERIFIER) SignerVerifier signerVerifier) { + return new JWTTokenStoreImpl(signer, signerVerifier); } }
