Skip to content

Commit eae337a

Browse files
authored
Merge pull request #1139 from WeBankPartners/dev
release v1.5.3
2 parents 88f119a + 1260263 commit eae337a

97 files changed

Lines changed: 3006 additions & 4346 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmdb-core/pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
<packaging>jar</packaging>
1414

1515
<dependencies>
16-
<dependency>
17-
<groupId>com.webank.wecube.platform</groupId>
18-
<artifactId>platform-auth-client</artifactId>
19-
<version>1.0-SNAPSHOT</version>
20-
</dependency>
2116
<dependency>
2217
<groupId>org.springframework.boot</groupId>
2318
<artifactId>spring-boot-starter-data-jpa</artifactId>
@@ -106,6 +101,11 @@
106101
<artifactId>commons-beanutils</artifactId>
107102
<version>1.9.4</version>
108103
</dependency>
104+
<dependency>
105+
<groupId>io.jsonwebtoken</groupId>
106+
<artifactId>jjwt</artifactId>
107+
<version>0.7.0</version>
108+
</dependency>
109109
<dependency>
110110
<groupId>io.springfox</groupId>
111111
<artifactId>springfox-swagger2</artifactId>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.webank.cmdb.authclient.filter;
2+
3+
import org.apache.commons.codec.binary.Base64;
4+
5+
import io.jsonwebtoken.Claims;
6+
import io.jsonwebtoken.Jws;
7+
import io.jsonwebtoken.JwtParser;
8+
import io.jsonwebtoken.Jwts;
9+
10+
/**
11+
*
12+
* @author gavinli
13+
*
14+
*/
15+
public class DefaultJwtSsoTokenParser implements JwtSsoTokenParser {
16+
17+
private static final String SIGNING_KEY = "Platform+Auth+Server+Secret";
18+
19+
private String jwtSigningKey;
20+
21+
private JwtParser jwtParser;
22+
23+
public DefaultJwtSsoTokenParser(String jwtSigningKey) {
24+
if (jwtSigningKey == null || jwtSigningKey.trim().length() < 1) {
25+
this.jwtSigningKey = SIGNING_KEY;
26+
} else {
27+
this.jwtSigningKey = jwtSigningKey;
28+
}
29+
30+
this.jwtParser = Jwts.parser().setSigningKey(decodeBase64(getJwtSigningKey()));
31+
}
32+
33+
@Override
34+
public Jws<Claims> parseJwt(String token) {
35+
return jwtParser.parseClaimsJws(token);
36+
}
37+
38+
private String getJwtSigningKey() {
39+
return jwtSigningKey;
40+
}
41+
42+
private byte[] decodeBase64(String base64String) {
43+
return Base64.decodeBase64(base64String);
44+
}
45+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.webank.cmdb.authclient.filter;
2+
3+
import java.io.IOException;
4+
5+
import javax.servlet.ServletException;
6+
import javax.servlet.http.HttpServletRequest;
7+
import javax.servlet.http.HttpServletResponse;
8+
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
import org.springframework.security.core.AuthenticationException;
12+
import org.springframework.security.web.AuthenticationEntryPoint;
13+
14+
import com.fasterxml.jackson.databind.ObjectMapper;
15+
16+
public class Http401AuthenticationEntryPoint implements AuthenticationEntryPoint {
17+
private static final Logger log = LoggerFactory.getLogger(Http401AuthenticationEntryPoint.class);
18+
19+
private static String HEADER_WWW_AUTHENTICATE = "WWW-Authenticate";
20+
21+
private String headerValue = "Bearer realm=\"Central Authentication Server\";profile=\"JWT\";";
22+
23+
private ObjectMapper objectMapper = new ObjectMapper();
24+
25+
@Override
26+
public void commence(HttpServletRequest request, HttpServletResponse response,
27+
AuthenticationException authException) throws IOException, ServletException {
28+
29+
log.warn("=== authentication failed === ");
30+
response.setHeader(HEADER_WWW_AUTHENTICATE, translateAuthenticateHeader(authException));
31+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
32+
33+
AuthenticationFailedResponse responseBody = AuthenticationFailedResponse.error(authException.getMessage());
34+
response.getOutputStream().println(objectMapper.writeValueAsString(responseBody));
35+
response.getOutputStream().flush();
36+
37+
}
38+
39+
protected String translateAuthenticateHeader(AuthenticationException e){
40+
StringBuilder sb = new StringBuilder();
41+
sb.append(this.headerValue);
42+
sb.append("error=\"").append(e.getMessage()).append("\";");
43+
44+
return sb.toString();
45+
}
46+
47+
static class AuthenticationFailedResponse {
48+
public final static String STATUS_OK = "OK";
49+
public final static String STATUS_ERROR = "ERROR";
50+
51+
public final static String OK_MESSAGE = "success";
52+
53+
private String status;
54+
private String message;
55+
private Object data;
56+
57+
public String getStatus() {
58+
return status;
59+
}
60+
61+
public void setStatus(String status) {
62+
this.status = status;
63+
}
64+
65+
public String getMessage() {
66+
return message;
67+
}
68+
69+
public void setMessage(String message) {
70+
this.message = message;
71+
}
72+
73+
public Object getData() {
74+
return data;
75+
}
76+
77+
public void setData(Object data) {
78+
this.data = data;
79+
}
80+
81+
public AuthenticationFailedResponse withData(Object data){
82+
this.data = data;
83+
return this;
84+
}
85+
86+
public static AuthenticationFailedResponse okay() {
87+
AuthenticationFailedResponse result = new AuthenticationFailedResponse();
88+
result.setStatus(STATUS_OK);
89+
result.setMessage(OK_MESSAGE);
90+
return result;
91+
}
92+
93+
public static AuthenticationFailedResponse okayWithData(Object data) {
94+
return okay().withData(data);
95+
}
96+
97+
public static AuthenticationFailedResponse error(String errorMessage) {
98+
AuthenticationFailedResponse result = new AuthenticationFailedResponse();
99+
result.setStatus(STATUS_ERROR);
100+
result.setMessage(errorMessage);
101+
return result;
102+
}
103+
}
104+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.webank.cmdb.authclient.filter;
2+
3+
public class JwtClientConfig {
4+
private String signingKey;
5+
6+
public String getSigningKey() {
7+
return signingKey;
8+
}
9+
10+
public void setSigningKey(String signingKey) {
11+
this.signingKey = signingKey;
12+
}
13+
14+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package com.webank.cmdb.authclient.filter;
2+
3+
import java.io.IOException;
4+
import java.util.ArrayList;
5+
6+
import javax.servlet.FilterChain;
7+
import javax.servlet.ServletException;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
11+
import org.apache.commons.lang3.StringUtils;
12+
import org.slf4j.Logger;
13+
import org.slf4j.LoggerFactory;
14+
import org.springframework.security.access.AccessDeniedException;
15+
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
16+
import org.springframework.security.authentication.AuthenticationManager;
17+
import org.springframework.security.authentication.BadCredentialsException;
18+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
19+
import org.springframework.security.core.AuthenticationException;
20+
import org.springframework.security.core.GrantedAuthority;
21+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
22+
import org.springframework.security.core.context.SecurityContextHolder;
23+
import org.springframework.security.web.AuthenticationEntryPoint;
24+
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
25+
26+
27+
import io.jsonwebtoken.Claims;
28+
import io.jsonwebtoken.ExpiredJwtException;
29+
import io.jsonwebtoken.Jws;
30+
import io.jsonwebtoken.JwtException;
31+
32+
public class JwtSsoBasedAuthenticationFilter extends BasicAuthenticationFilter {
33+
private static final Logger log = LoggerFactory.getLogger(JwtSsoBasedAuthenticationFilter.class);
34+
35+
private static String HEADER_AUTHORIZATION = "Authorization";
36+
private static String PREFIX_BEARER_TOKEN = "Bearer ";
37+
private static String CLAIM_KEY_TYPE = "type";
38+
private static String CLAIM_KEY_AUTHORITIES = "authority";
39+
private static String TOKEN_TYPE_ACCESS = "accessToken";
40+
41+
private JwtSsoTokenParser jwtParser = null;
42+
43+
private boolean ignoreFailure = false;
44+
45+
public JwtSsoBasedAuthenticationFilter(AuthenticationManager authenticationManager, JwtClientConfig jwtClientConfig) {
46+
super(authenticationManager);
47+
this.ignoreFailure = true;
48+
this.jwtParser = new DefaultJwtSsoTokenParser(jwtClientConfig.getSigningKey());
49+
}
50+
51+
public JwtSsoBasedAuthenticationFilter(AuthenticationManager authenticationManager,
52+
AuthenticationEntryPoint authenticationEntryPoint, JwtClientConfig jwtClientConfig) {
53+
super(authenticationManager, authenticationEntryPoint);
54+
this.jwtParser = new DefaultJwtSsoTokenParser(jwtClientConfig.getSigningKey());
55+
}
56+
57+
@Override
58+
public void afterPropertiesSet() {
59+
super.afterPropertiesSet();
60+
61+
if (log.isInfoEnabled()) {
62+
log.info("Filter:{} applied", this.getClass().getSimpleName());
63+
}
64+
}
65+
66+
@Override
67+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
68+
throws IOException, ServletException {
69+
if (log.isDebugEnabled()) {
70+
log.debug("=== doFilterInternal ===");
71+
}
72+
SecurityContextHolder.clearContext();
73+
74+
String header = request.getHeader(HEADER_AUTHORIZATION);
75+
if (header == null || !header.startsWith(PREFIX_BEARER_TOKEN)) {
76+
77+
log.debug("bearer token does not exist");
78+
79+
chain.doFilter(request, response);
80+
81+
return;
82+
}
83+
84+
if (log.isDebugEnabled()) {
85+
log.debug("start to authenticate with bearer token");
86+
}
87+
88+
try {
89+
UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
90+
if (authentication != null && authentication.isAuthenticated()) {
91+
SecurityContextHolder.getContext().setAuthentication(authentication);
92+
}
93+
} catch (AuthenticationException failed) {
94+
log.debug("authentication failed");
95+
96+
SecurityContextHolder.clearContext();
97+
98+
onUnsuccessfulAuthentication(request, response, failed);
99+
100+
if (this.ignoreFailure) {
101+
chain.doFilter(request, response);
102+
} else {
103+
this.getAuthenticationEntryPoint().commence(request, response, failed);
104+
}
105+
106+
return;
107+
108+
}
109+
executeFilter(request, response, chain);
110+
}
111+
112+
protected void executeFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
113+
throws IOException, ServletException {
114+
try {
115+
chain.doFilter(request, response);
116+
} finally {
117+
SecurityContextHolder.clearContext();
118+
}
119+
}
120+
121+
protected boolean isIgnoreFailure() {
122+
return this.ignoreFailure;
123+
}
124+
125+
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
126+
AuthenticationException failed) throws IOException {
127+
}
128+
129+
protected void validateRequestHeader(HttpServletRequest request) {
130+
String header = request.getHeader(HEADER_AUTHORIZATION);
131+
if (header == null || !header.startsWith(PREFIX_BEARER_TOKEN)) {
132+
throw new BadCredentialsException("Access token is required.");
133+
}
134+
}
135+
136+
protected UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
137+
validateRequestHeader(request);
138+
139+
String sAccessTokenHeader = request.getHeader(HEADER_AUTHORIZATION);
140+
141+
String sAccessToken = sAccessTokenHeader.substring(PREFIX_BEARER_TOKEN.length()).trim();
142+
143+
if (StringUtils.isBlank(sAccessToken)) {
144+
throw new AuthenticationCredentialsNotFoundException("Access token is blank.");
145+
}
146+
147+
Jws<Claims> jwt = null;
148+
try {
149+
jwt = jwtParser.parseJwt(sAccessToken);
150+
} catch (ExpiredJwtException e) {
151+
throw new BadCredentialsException("Access token has expired.");
152+
} catch (JwtException e) {
153+
throw new BadCredentialsException("Access token is not available.");
154+
}
155+
156+
Claims claims = jwt.getBody();
157+
158+
String sAuthorities = claims.get(CLAIM_KEY_AUTHORITIES, String.class);
159+
160+
String username = claims.getSubject();
161+
162+
log.info("subject:{}", username);
163+
164+
String tokenType = claims.get(CLAIM_KEY_TYPE, String.class);
165+
166+
if (!TOKEN_TYPE_ACCESS.equals(tokenType)) {
167+
throw new AccessDeniedException("Access token is required.");
168+
}
169+
170+
if (sAuthorities.length() >= 2) {
171+
sAuthorities = sAuthorities.substring(1);
172+
sAuthorities = sAuthorities.substring(0, sAuthorities.length() - 1);
173+
}
174+
175+
log.info("Authority String:{}", sAuthorities);
176+
177+
ArrayList<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
178+
179+
if (StringUtils.isNotBlank(sAuthorities)) {
180+
String[] aAuthParts = sAuthorities.split(",");
181+
for (String s : aAuthParts) {
182+
GrantedAuthority ga = new SimpleGrantedAuthority(s.trim());
183+
authorities.add(ga);
184+
}
185+
}
186+
187+
log.info("Authorities:{}", authorities);
188+
189+
return new UsernamePasswordAuthenticationToken(username, sAccessTokenHeader, authorities);
190+
191+
}
192+
193+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.webank.cmdb.authclient.filter;
2+
3+
import io.jsonwebtoken.Claims;
4+
import io.jsonwebtoken.Jws;
5+
6+
public interface JwtSsoTokenParser {
7+
Jws<Claims> parseJwt(String token);
8+
}

cmdb-core/src/main/java/com/webank/cmdb/config/ApplicationProperties.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,6 @@ public class SecurityProperties {
6767
private String casServerUrl;
6868
private String casRedirectAppAddr;
6969
private String whitelistIpAddress;
70+
private String jwtSigningKey = "Platform+Auth+Server+Secret";
7071
}
7172
}

0 commit comments

Comments
 (0)