Secure Spring Boot 2 Resource Server with OAuth2 and Redis Token Store
This guide demonstrates how to integrate Spring Boot 2 with OAuth2 to build a protected resource server, configure Redis for token storage, define domain and security classes, and test the secured endpoints, complete with code snippets and visual verification steps.
Continuing from the previous article on integrating OAuth2 authentication service, this tutorial shows how to use the issued token to protect resources.
Environment : Spring Boot 2.2.11.RELEASE, OAuth2, Redis.
pom.xml dependencies
<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.11.RELEASE</version>
</dependency></code>application.yml configuration
<code>server:
port: 8088
---
spring:
application:
name: oauth-resource
---
spring:
redis:
host: localhost
port: 6379
password:
database: 1
lettuce:
pool:
maxActive: 8
maxIdle: 100
minIdle: 10
maxWait: -1</code>Domain object
<code>public class Users implements UserDetails, Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String username;
private String password;
}</code>Core configuration class
<code>@Configuration
@EnableResourceServer
public class OAuthConfig extends ResourceServerConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(OAuthConfig.class);
public static final String RESOURCE_ID = "gx_resource_id";
@Resource
private RedisConnectionFactory redisConnectionFactory;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID);
OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
oAuth2AuthenticationEntryPoint.setExceptionTranslator(webResponseExceptionTranslator());
resources.authenticationEntryPoint(oAuth2AuthenticationEntryPoint);
resources.tokenExtractor(request -> {
String tokenValue = extractToken(request);
if (tokenValue != null) {
return new PreAuthenticatedAuthenticationToken(tokenValue, "");
}
return null;
});
}
private String extractToken(HttpServletRequest request) {
// first check the header... Authorization: Bearer xxx
String token = extractHeaderToken(request);
// second check the header... access_token: xxx
if (token == null) {
token = request.getHeader("access_token");
}
// bearer type allows a request parameter as well
if (token == null) {
logger.debug("Token not found in headers. Trying request parameters.");
token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
if (token == null) {
logger.debug("Token not found in request parameters. Not an OAuth2 request.");
} else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
}
}
return token;
}
private String extractHeaderToken(HttpServletRequest request) {
Enumeration<String> headers = request.getHeaders("Authorization");
while (headers.hasMoreElements()) {
String value = headers.nextElement();
if (value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase())) {
String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
int commaIndex = authHeaderValue.indexOf(',');
if (commaIndex > 0) {
authHeaderValue = authHeaderValue.substring(0, commaIndex);
}
return authHeaderValue;
}
}
return null;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatcher(request -> {
String path = request.getServletPath();
if (path != null && path.startsWith("/demo")) {
return true;
}
return false;
}).authorizeRequests().anyRequest().authenticated();
}
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
@Bean
public WebResponseExceptionTranslator<?> webResponseExceptionTranslator() {
return new DefaultWebResponseExceptionTranslator() {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public ResponseEntity translate(Exception e) throws Exception {
ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
return exceptionProcess(responseEntity);
}
};
}
private static ResponseEntity<Map<String, Object>> exceptionProcess(ResponseEntity<OAuth2Exception> responseEntity) {
Map<String, Object> body = new HashMap<>();
body.put("code", -1);
OAuth2Exception excep = responseEntity.getBody();
String errorMessage = excep.getMessage();
if (errorMessage != null) {
errorMessage = "Authentication failed, illegal user";
body.put("message", errorMessage);
} else {
String error = excep.getOAuth2ErrorCode();
if (error != null) {
body.put("message", error);
} else {
body.put("message", "Authentication service exception, unknown error");
}
}
body.put("data", null);
return new ResponseEntity<>(body, responseEntity.getHeaders(), responseEntity.getStatusCode());
}
}
</code>Test controller
<code>@RestController
@RequestMapping("/demo")
public class DemoController {
@GetMapping("/res")
public Object res() {
return "success";
}
}</code>Testing steps:
1. Access the endpoint with no token or an invalid token – the request is rejected.
2. Obtain a valid token from the authentication service.
3. Use the token to call the protected
/demo/resendpoint – the response returns
success.
The integration works correctly.
End of tutorial.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.