Refresh token endpoint.

This commit is contained in:
svlada 2016-08-19 16:58:25 +02:00
parent a3e9b93382
commit f1a5be7412
8 changed files with 81 additions and 26 deletions

View File

@ -1,6 +1,8 @@
package com.svlada.security.auth.ajax;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -43,12 +45,16 @@ public class AjaxAwareAuthenticationSuccessHandler implements AuthenticationSucc
Authentication authentication) throws IOException, ServletException {
UserContext userContext = (UserContext) authentication.getPrincipal();
JwtToken token = tokenFactory.createAccessJwtToken(userContext);
JwtToken accessToken = tokenFactory.createAccessJwtToken(userContext);
JwtToken refreshToken = tokenFactory.createRefreshToken(userContext);
Map<String, String> tokenMap = new HashMap<String, String>();
tokenMap.put("token", accessToken.getToken());
tokenMap.put("refreshToken", refreshToken.getToken());
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(response.getWriter(), token);
mapper.writeValue(response.getWriter(), tokenMap);
clearAuthenticationAttributes(request);
}

View File

@ -39,11 +39,9 @@ public class AjaxLoginProcessingFilter extends AbstractAuthenticationProcessingF
private final ObjectMapper objectMapper;
public AjaxLoginProcessingFilter(String defaultFilterProcessesUrl,
AuthenticationSuccessHandler successHandler,
AuthenticationFailureHandler failureHandler,
ObjectMapper mapper) {
super(defaultFilterProcessesUrl);
public AjaxLoginProcessingFilter(String defaultProcessUrl, AuthenticationSuccessHandler successHandler,
AuthenticationFailureHandler failureHandler, ObjectMapper mapper) {
super(defaultProcessUrl);
this.successHandler = successHandler;
this.failureHandler = failureHandler;
this.objectMapper = mapper;

View File

@ -14,6 +14,7 @@ import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.RequestMatcher;
import com.svlada.security.auth.JwtAuthenticationToken;
import com.svlada.security.auth.jwt.extractor.TokenExtractor;
@ -33,9 +34,8 @@ public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticati
@Autowired
public JwtTokenAuthenticationProcessingFilter(AuthenticationFailureHandler failureHandler,
TokenExtractor tokenExtractor,
String filterProcessingUrl) {
super(filterProcessingUrl);
TokenExtractor tokenExtractor, RequestMatcher matcher) {
super(matcher);
this.failureHandler = failureHandler;
this.tokenExtractor = tokenExtractor;
}

View File

@ -0,0 +1,38 @@
package com.svlada.security.auth.jwt;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* SkipPathRequestMatcher
*
* @author vladimir.stankovic
*
* Aug 19, 2016
*/
public class SkipPathRequestMatcher implements RequestMatcher {
private OrRequestMatcher matchers;
private RequestMatcher processingMatcher;
public SkipPathRequestMatcher(List<String> pathsToSkip, String processingPath) {
Assert.notNull(pathsToSkip);
List<RequestMatcher> m = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList());
matchers = new OrRequestMatcher(m);
processingMatcher = new AntPathRequestMatcher(processingPath);
}
@Override
public boolean matches(HttpServletRequest request) {
if (matchers.matches(request)) {
return false;
}
return processingMatcher.matches(request) ? true : false;
}
}

View File

@ -1,5 +1,8 @@
package com.svlada.security.config;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -20,8 +23,8 @@ import com.svlada.security.auth.ajax.AjaxAuthenticationProvider;
import com.svlada.security.auth.ajax.AjaxLoginProcessingFilter;
import com.svlada.security.auth.jwt.JwtAuthenticationProvider;
import com.svlada.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
import com.svlada.security.auth.jwt.SkipPathRequestMatcher;
import com.svlada.security.auth.jwt.extractor.TokenExtractor;
import com.svlada.security.model.token.JwtTokenFactory;
/**
* WebSecurityConfig
@ -45,12 +48,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired private JwtAuthenticationProvider jwtAuthenticationProvider;
@Autowired private TokenExtractor tokenExtractor;
@Autowired private JwtTokenFactory tokenFactory;
@Autowired private AuthenticationManager authenticationManager;
@Autowired private ObjectMapper objectMapper;
@Bean
protected AjaxLoginProcessingFilter buildAjaxLoginProcessingFilter() throws Exception {
AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
@ -60,7 +62,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
JwtTokenAuthenticationProcessingFilter filter = new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenFactory, tokenExtractor, TOKEN_BASED_AUTH_ENTRY_POINT);
List<String> pathsToSkip = Arrays.asList(TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT);
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}

View File

@ -5,10 +5,12 @@ import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.GrantedAuthority;
@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.svlada.entity.User;
import com.svlada.security.UserService;
import com.svlada.security.auth.jwt.extractor.TokenExtractor;
import com.svlada.security.auth.jwt.verifier.TokenVerifier;
import com.svlada.security.config.JwtSettings;
import com.svlada.security.config.WebSecurityConfig;
@ -44,12 +47,15 @@ public class RefreshTokenEndpoint {
@Autowired private JwtSettings jwtSettings;
@Autowired private UserService userService;
@Autowired private TokenVerifier tokenVerifier;
@Autowired @Qualifier("jwtHeaderTokenExtractor") private TokenExtractor tokenExtractor;
@RequestMapping(value="/api/auth/token", method=RequestMethod.GET, produces={ MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody JwtToken refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
RawAccessJwtToken rawToken = new RawAccessJwtToken(request.getHeader(WebSecurityConfig.JWT_TOKEN_HEADER_PARAM));
RefreshToken refreshToken = RefreshToken.create(rawToken, jwtSettings.getTokenSigningKey()).orElseThrow(() -> new InvalidJwtToken());
String tokenPayload = tokenExtractor.extract(request.getHeader(WebSecurityConfig.JWT_TOKEN_HEADER_PARAM));
RawAccessJwtToken rawToken = new RawAccessJwtToken(tokenPayload);
RefreshToken refreshToken = RefreshToken.create(rawToken, jwtSettings.getTokenSigningKey()).orElseThrow(() -> new InvalidJwtToken());
String jti = refreshToken.getJti();
if (!tokenVerifier.verify(jti)) {
throw new InvalidJwtToken();

View File

@ -22,12 +22,12 @@ import io.jsonwebtoken.SignatureAlgorithm;
*
* @author vladimir.stankovic
*
* May 31, 2016
* May 31, 2016
*/
@Component
public class JwtTokenFactory {
private final JwtSettings settings;
@Autowired
public JwtTokenFactory(JwtSettings settings) {
this.settings = settings;
@ -43,7 +43,7 @@ public class JwtTokenFactory {
public AccessJwtToken createAccessJwtToken(UserContext userContext) {
if (StringUtils.isBlank(userContext.getUsername()))
throw new IllegalArgumentException("Cannot create JWT Token without username");
if (userContext.getAuthorities() == null || userContext.getAuthorities().isEmpty())
throw new IllegalArgumentException("User doesn't have any privileges");
@ -51,7 +51,7 @@ public class JwtTokenFactory {
claims.put("scopes", userContext.getAuthorities().stream().map(s -> s.toString()).collect(Collectors.toList()));
DateTime currentTime = new DateTime();
String token = Jwts.builder()
.setClaims(claims)
.setIssuer(settings.getTokenIssuer())
@ -71,7 +71,7 @@ public class JwtTokenFactory {
DateTime currentTime = new DateTime();
Claims claims = Jwts.claims().setSubject(userContext.getUsername());
claims.put("scopes", Arrays.asList(Scopes.REFRESH_TOKEN));
claims.put("scopes", Arrays.asList(Scopes.REFRESH_TOKEN.authority()));
String token = Jwts.builder()
.setClaims(claims)

View File

@ -12,18 +12,20 @@ import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
/**
* RefreshToken
*
* @author vladimir.stankovic
*
* Aug 19, 2016
*/
@SuppressWarnings("unchecked")
public class RefreshToken implements JwtToken {
private Jws<Claims> claims;
private RefreshToken(Jws<Claims> claims) {
this.claims = claims;
}
/**
* Creates and validates Refresh token
*
@ -43,10 +45,10 @@ public class RefreshToken implements JwtToken {
|| !scopes.stream().filter(scope -> Scopes.REFRESH_TOKEN.authority().equals(scope)).findFirst().isPresent()) {
return Optional.empty();
}
return Optional.of(new RefreshToken(claims));
}
@Override
public String getToken() {
return null;