Skip to content

Commit f1663b3

Browse files
committed
feat(uaa-web): Authentication handler for OAuth2 Server
1 parent 48db5f2 commit f1663b3

File tree

7 files changed

+322
-1
lines changed

7 files changed

+322
-1
lines changed

uaa/uaa-web/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
<groupId>org.springframework.boot</groupId>
5050
<artifactId>spring-boot-starter-jdbc</artifactId>
5151
</dependency>
52+
<dependency>
53+
<groupId>org.springframework</groupId>
54+
<artifactId>spring-test</artifactId>
55+
</dependency>
5256
<dependency>
5357
<groupId>com.h2database</groupId>
5458
<artifactId>h2</artifactId>

uaa/uaa-web/src/main/java/sanchez/sergio/config/security/AuthServerOAuth2Config.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33

44
import java.util.ArrayList;
5-
import java.util.Arrays;
65
import java.util.List;
76
import javax.annotation.PostConstruct;
87
import javax.sql.DataSource;

uaa/uaa-web/src/main/java/sanchez/sergio/config/security/UaaWebSecurityConfiguration.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1818
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
1919
import org.springframework.security.config.http.SessionCreationPolicy;
20+
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
21+
import sanchez.sergio.security.filter.OAuth2ServerAuthenticationFilter;
2022

2123
@Configuration
2224
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@@ -26,6 +28,9 @@ public class UaaWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
2628

2729
@Autowired
2830
private AuthenticationProvider authenticationProvider;
31+
32+
@Autowired
33+
private OAuth2ServerAuthenticationFilter authenticationFilter;
2934

3035
@Override
3136
@Bean
@@ -44,6 +49,7 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
4449
protected void configure(HttpSecurity http) throws Exception {
4550

4651
http
52+
.addFilterBefore(authenticationFilter, BasicAuthenticationFilter.class)
4753
.exceptionHandling()
4854
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
4955
.and()
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package sanchez.sergio.security.filter;
2+
3+
import com.fasterxml.jackson.core.JsonParseException;
4+
import com.fasterxml.jackson.databind.JsonMappingException;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import java.io.IOException;
7+
import org.springframework.security.oauth2.common.OAuth2AccessToken;
8+
9+
/**
10+
* @author Sergio Sánchez
11+
* this is the implementation for extract access_token from response of oauth/token response
12+
* this will work fine for password flow
13+
*/
14+
public class DefaultOAuth2AccessTokenExtractor implements OAuth2AccessTokenExtractor {
15+
16+
private ObjectMapper mapper = new ObjectMapper();
17+
18+
@Override
19+
public String getAccessTokenValue(byte[] response) {
20+
try {
21+
return mapper.readValue(response, OAuth2AccessToken.class)
22+
.getValue();
23+
} catch (JsonParseException e) {
24+
e.printStackTrace();
25+
} catch (JsonMappingException e) {
26+
e.printStackTrace();
27+
} catch (IOException e) {
28+
e.printStackTrace();
29+
}
30+
return null;
31+
}
32+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package sanchez.sergio.security.filter;
2+
3+
/**
4+
* @author Sergio Sánchez
5+
* To extractor access_token from response of /token
6+
*/
7+
public interface OAuth2AccessTokenExtractor {
8+
String getAccessTokenValue(byte[] response);
9+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package sanchez.sergio.security.filter;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.io.IOException;
5+
6+
import javax.servlet.Filter;
7+
import javax.servlet.FilterChain;
8+
import javax.servlet.FilterConfig;
9+
import javax.servlet.ServletException;
10+
import javax.servlet.ServletOutputStream;
11+
import javax.servlet.ServletRequest;
12+
import javax.servlet.ServletResponse;
13+
import javax.servlet.http.HttpServletResponse;
14+
import javax.servlet.http.HttpServletResponseWrapper;
15+
16+
import org.apache.commons.io.output.TeeOutputStream;
17+
import org.apache.log4j.Logger;
18+
import org.springframework.mock.web.DelegatingServletOutputStream;
19+
import org.springframework.security.core.Authentication;
20+
import org.springframework.security.core.context.SecurityContextHolder;
21+
import org.springframework.security.oauth2.common.OAuth2AccessToken;
22+
import org.springframework.security.oauth2.common.util.OAuth2Utils;
23+
import org.springframework.security.oauth2.provider.OAuth2Authentication;
24+
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
25+
26+
/**
27+
* @author Sergio Sánchez
28+
* Filter to handle authentication of server
29+
*
30+
*/
31+
public abstract class OAuth2ServerAuthenticationFilter implements Filter {
32+
33+
private static Logger log = Logger
34+
.getLogger(OAuth2ServerAuthenticationFilter.class);
35+
36+
private DefaultTokenServices tokenServices;
37+
38+
private OAuth2AccessTokenExtractor tokenExtractor;
39+
40+
public OAuth2ServerAuthenticationFilter(DefaultTokenServices tokenServices) {
41+
this.tokenServices = tokenServices;
42+
this.tokenExtractor = new DefaultOAuth2AccessTokenExtractor();
43+
}
44+
45+
public OAuth2ServerAuthenticationFilter(DefaultTokenServices tokenServices,
46+
OAuth2AccessTokenExtractor tokenExtractor) {
47+
this.tokenServices = tokenServices;
48+
this.tokenExtractor = tokenExtractor;
49+
}
50+
51+
@Override
52+
public void doFilter(ServletRequest request, ServletResponse response,
53+
FilterChain chain) throws IOException, ServletException {
54+
55+
// create wrapper to read response body
56+
ByteArrayResponseWrapper responseWraper = new ByteArrayResponseWrapper(
57+
response);
58+
59+
// led them go
60+
chain.doFilter(request, responseWraper);
61+
62+
// get ClientAuthentication
63+
Authentication clientAuthentication = SecurityContextHolder
64+
.getContext().getAuthentication();
65+
66+
// is authenticated or not to proceed
67+
if (clientAuthentication != null
68+
&& clientAuthentication.isAuthenticated()) {
69+
70+
// callBack client authenticated successfully
71+
onSuccessfulClientAuthentication(request, response,
72+
clientAuthentication);
73+
74+
// check response status is success of failure
75+
if (responseWraper.getStatus() == 200) {
76+
77+
// extract accessToken from response
78+
String token = tokenExtractor
79+
.getAccessTokenValue(responseWraper.getByteArray());
80+
81+
if (token != null && !token.isEmpty()) {
82+
83+
// load authentication from token
84+
OAuth2Authentication oAuth2Authentication = this.tokenServices
85+
.loadAuthentication(token);
86+
OAuth2AccessToken actualAccessToken = this.tokenServices
87+
.getAccessToken(oAuth2Authentication);
88+
89+
// callBack user authenticated successfully
90+
onSuccessfulUserAuthentication(request, response,
91+
clientAuthentication, oAuth2Authentication,
92+
actualAccessToken);
93+
} else {
94+
log.error("access token is empty from extractor");
95+
}
96+
} else {
97+
// callBack user authenticated failure
98+
onFailureUserAuthentication(request, response,
99+
clientAuthentication, request.getParameter("username"));
100+
}
101+
} else {
102+
// callBack client authenticated failure
103+
onFailClientAuthentication(request, response,
104+
request.getParameter(OAuth2Utils.CLIENT_ID));
105+
}
106+
}
107+
108+
protected void onSuccessfulClientAuthentication(ServletRequest request,
109+
ServletResponse response, Authentication authentication) {
110+
}
111+
112+
protected void onFailClientAuthentication(ServletRequest request,
113+
ServletResponse response, String clientId) {
114+
}
115+
116+
protected void onSuccessfulUserAuthentication(ServletRequest request,
117+
ServletResponse response, Authentication clientAuthentication,
118+
OAuth2Authentication userOAuth2Authentication,
119+
OAuth2AccessToken token) {
120+
}
121+
122+
protected void onFailureUserAuthentication(ServletRequest request,
123+
ServletResponse response, Authentication clientAuthentication,
124+
String username) {
125+
}
126+
127+
@Override
128+
public void init(FilterConfig filterConfig) throws ServletException {
129+
130+
}
131+
132+
@Override
133+
public void destroy() {
134+
135+
}
136+
137+
public DefaultTokenServices getTokenServices() {
138+
return tokenServices;
139+
}
140+
141+
public void setTokenServices(DefaultTokenServices tokenServices) {
142+
this.tokenServices = tokenServices;
143+
}
144+
145+
public OAuth2AccessTokenExtractor getTokenExtractor() {
146+
return tokenExtractor;
147+
}
148+
149+
public void setTokenExtractor(OAuth2AccessTokenExtractor tokenExtractor) {
150+
this.tokenExtractor = tokenExtractor;
151+
}
152+
153+
// response wrapper
154+
private class ByteArrayResponseWrapper extends HttpServletResponseWrapper {
155+
156+
public ByteArrayResponseWrapper(ServletResponse response) {
157+
super((HttpServletResponse) response);
158+
}
159+
160+
private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
161+
162+
@Override
163+
public ServletOutputStream getOutputStream() throws IOException {
164+
return new DelegatingServletOutputStream(new TeeOutputStream(
165+
super.getOutputStream(), byteArrayOutputStream));
166+
}
167+
168+
public byte[] getByteArray() {
169+
return this.byteArrayOutputStream.toByteArray();
170+
}
171+
}
172+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package sanchez.sergio.security.filter;
2+
3+
import javax.annotation.PostConstruct;
4+
import javax.servlet.ServletRequest;
5+
import javax.servlet.ServletResponse;
6+
import javax.servlet.http.HttpServletRequest;
7+
8+
import org.apache.log4j.Logger;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.security.core.Authentication;
11+
import org.springframework.security.oauth2.common.OAuth2AccessToken;
12+
import org.springframework.security.oauth2.provider.OAuth2Authentication;
13+
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
14+
import org.springframework.security.oauth2.provider.token.TokenStore;
15+
import org.springframework.stereotype.Component;
16+
import org.springframework.util.Assert;
17+
18+
/**
19+
* @author Sergio Sánchez
20+
* Authentication handler for server.
21+
*
22+
*/
23+
@Component
24+
public class OAuth2ServerAuthenticationFilterImpl extends OAuth2ServerAuthenticationFilter {
25+
26+
private static final Logger logger = Logger.getLogger(OAuth2ServerAuthenticationFilterImpl.class);
27+
28+
@Autowired
29+
private TokenStore tokenStore;
30+
31+
public OAuth2ServerAuthenticationFilterImpl(DefaultTokenServices tokenServices) {
32+
super(tokenServices);
33+
}
34+
35+
@Override
36+
protected void onSuccessfulUserAuthentication(ServletRequest request,
37+
ServletResponse response, Authentication clientAuthentication,
38+
OAuth2Authentication userOAuth2Authentication,
39+
OAuth2AccessToken token) {
40+
41+
// To skip refresh_token request
42+
if (userOAuth2Authentication.getOAuth2Request()
43+
.getRefreshTokenRequest() == null) {
44+
45+
Authentication userAuthentication = (Authentication) userOAuth2Authentication.getUserAuthentication();
46+
if(userAuthentication != null) {
47+
48+
logger.debug("on Successful User Authentication- " + userAuthentication.getName());
49+
}
50+
}
51+
}
52+
53+
@Override
54+
protected void onFailureUserAuthentication(ServletRequest request,
55+
ServletResponse response, Authentication clientAuthentication,
56+
String username) {
57+
logger.debug("on fail user authenticaion -" + username);
58+
}
59+
60+
@Override
61+
protected void onFailClientAuthentication(ServletRequest request,
62+
ServletResponse response, String clientId) {
63+
logger.debug("on fail Client Authentication -" + clientId);
64+
}
65+
66+
@Override
67+
protected void onSuccessfulClientAuthentication(ServletRequest request,
68+
ServletResponse response, Authentication authentication) {
69+
logger.debug("on Successful Client Authentication -" + authentication);
70+
}
71+
72+
private String extractIp(ServletRequest request) {
73+
String ipAddress = null;
74+
if (request instanceof HttpServletRequest) {
75+
HttpServletRequest req = (HttpServletRequest) request;
76+
ipAddress = req.getHeader("X-FORWARDED-FOR");
77+
if (ipAddress == null) {
78+
ipAddress = request.getRemoteAddr();
79+
}
80+
}
81+
return ipAddress;
82+
}
83+
84+
private String extractUserAgent(ServletRequest request) {
85+
String userAgent = null;
86+
if (request instanceof HttpServletRequest) {
87+
userAgent = ((HttpServletRequest) request).getHeader("User-Agent")
88+
.toLowerCase();
89+
}
90+
return userAgent;
91+
}
92+
93+
@PostConstruct
94+
public void init(){
95+
logger.info("Init OAuth2Server Authentication Filter ...");
96+
Assert.state(tokenStore != null, "A TokenStore must be provided");
97+
}
98+
99+
}

0 commit comments

Comments
 (0)