Refresh token endpoint.
This commit is contained in:
parent
a3e9b93382
commit
f1a5be7412
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user