This commit has compile errors. I'm in a hurry to catch a train.
This commit is contained in:
parent
9cb3386e86
commit
f8af4297bc
@ -413,7 +413,7 @@ public class AjaxAwareAuthenticationFailureHandler implements AuthenticationFail
|
|||||||
|
|
||||||
#### WebSecurityConfig - Initial version to support AJAX based login
|
#### WebSecurityConfig - Initial version to support AJAX based login
|
||||||
|
|
||||||
This is first version of WebSecurityConfig. We will add more configuration to it once we start with showcase of JWT Authentication flow.
|
This is first version of WebSecurityConfig. We will add more configuration to it once we start with showcase of JWT Authentication flow.
|
||||||
|
|
||||||
```
|
```
|
||||||
@Configuration
|
@Configuration
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
package com.svlada.security.auth.jwt;
|
package com.svlada.security.auth.jwt;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.Minutes;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.AuthenticationProvider;
|
import org.springframework.security.authentication.AuthenticationProvider;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
@ -17,10 +20,14 @@ import com.svlada.security.auth.JwtAuthenticationToken;
|
|||||||
import com.svlada.security.config.JwtSettings;
|
import com.svlada.security.config.JwtSettings;
|
||||||
import com.svlada.security.exceptions.JwtExpiredTokenException;
|
import com.svlada.security.exceptions.JwtExpiredTokenException;
|
||||||
import com.svlada.security.model.JwtToken;
|
import com.svlada.security.model.JwtToken;
|
||||||
|
import com.svlada.security.model.SafeJwtToken;
|
||||||
import com.svlada.security.model.UnsafeJwtToken;
|
import com.svlada.security.model.UnsafeJwtToken;
|
||||||
|
import com.svlada.security.model.UserContext;
|
||||||
|
import com.svlada.security.service.UserService;
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.ExpiredJwtException;
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
import io.jsonwebtoken.MalformedJwtException;
|
import io.jsonwebtoken.MalformedJwtException;
|
||||||
import io.jsonwebtoken.SignatureException;
|
import io.jsonwebtoken.SignatureException;
|
||||||
import io.jsonwebtoken.UnsupportedJwtException;
|
import io.jsonwebtoken.UnsupportedJwtException;
|
||||||
@ -35,36 +42,37 @@ import io.jsonwebtoken.UnsupportedJwtException;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class JwtAuthenticationProvider implements AuthenticationProvider {
|
public class JwtAuthenticationProvider implements AuthenticationProvider {
|
||||||
private final JwtSettings jwtSettings;
|
private final TokenAuthStrategy tokenAuthStrategy;
|
||||||
private final TokenAuthStrategy tokenAuthStrategy;
|
private final UserService userService;
|
||||||
|
private final JwtSettings jwtSettings;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public JwtAuthenticationProvider(JwtSettings jwtSettings, TokenAuthStrategy tokenAuthStrategy) {
|
public JwtAuthenticationProvider(TokenAuthStrategy tokenAuthStrategy, UserService userService, JwtSettings jwtSettings) {
|
||||||
this.jwtSettings = jwtSettings;
|
|
||||||
this.tokenAuthStrategy = tokenAuthStrategy;
|
this.tokenAuthStrategy = tokenAuthStrategy;
|
||||||
|
this.userService = userService;
|
||||||
|
this.jwtSettings = jwtSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||||
UnsafeJwtToken token = ((JwtAuthenticationToken) authentication).getUnsafeToken();
|
UnsafeJwtToken unsafeToken = ((JwtAuthenticationToken) authentication).getUnsafeToken();
|
||||||
|
|
||||||
SafeToken safeToken = token.authenticate(tokenAuthStrategy);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
token.validateToken(jwtSettings.getTokenSigningKey());
|
Jws<Claims> jwsClaims = unsafeToken.parse(jwtSettings.getTokenSigningKey());
|
||||||
} catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {
|
} catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {
|
||||||
throw new BadCredentialsException("Invalid JWT token: ", ex);
|
throw new BadCredentialsException("Invalid JWT token: ", ex);
|
||||||
} catch (ExpiredJwtException expiredEx) {
|
} catch (ExpiredJwtException expiredEx) {
|
||||||
throw new JwtExpiredTokenException(token, "Token expired.", expiredEx);
|
Date expDateTime = expiredEx.getClaims().getExpiration();
|
||||||
}
|
|
||||||
|
if (expDate != null && tokenAuthStrategy.isExpired(expDate)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Claims claims = token.claims(jwtSettings.getTokenSigningKey());
|
SafeJwtToken safeToken = ;
|
||||||
ArrayList<String> rawAuthorities = claims.get("roles", ArrayList.class);
|
Claims claims = safeToken.getClaims();
|
||||||
|
|
||||||
List<GrantedAuthority> authorities = rawAuthorities.stream()
|
JwtAuthenticationToken authToken = new JwtAuthenticationToken(userContext, safeToken, userContext.getAuthorities());
|
||||||
.map(authority -> new SimpleGrantedAuthority(authority)).collect(Collectors.toList());
|
|
||||||
|
|
||||||
JwtAuthenticationToken authToken = new JwtAuthenticationToken(token, authorities, claims.getSubject());
|
|
||||||
|
|
||||||
return authToken;
|
return authToken;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
package com.svlada.security.auth.jwt;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.joda.time.DateTime;
|
||||||
|
import org.joda.time.Minutes;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import com.svlada.security.config.JwtSettings;
|
||||||
|
import com.svlada.security.exceptions.JwtExpiredTokenException;
|
||||||
|
import com.svlada.security.model.JwtTokenFactory;
|
||||||
|
import com.svlada.security.model.SafeJwtToken;
|
||||||
|
import com.svlada.security.model.UnsafeJwtToken;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
|
import io.jsonwebtoken.MalformedJwtException;
|
||||||
|
import io.jsonwebtoken.SignatureException;
|
||||||
|
import io.jsonwebtoken.UnsupportedJwtException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author vladimir.stankovic
|
||||||
|
*
|
||||||
|
* Aug 5, 2016
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class RefreshTokenAuthStrategy implements TokenAuthStrategy {
|
||||||
|
private final JwtSettings jwtSettings;
|
||||||
|
private final JwtTokenFactory tokenFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public RefreshTokenAuthStrategy(JwtSettings jwtSettings, JwtTokenFactory tokenFactory) {
|
||||||
|
this.jwtSettings = jwtSettings;
|
||||||
|
this.tokenFactory = tokenFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SafeJwtToken authenticate(UnsafeJwtToken token) {
|
||||||
|
try {
|
||||||
|
Jws<Claims> jwsClaims = token.parse(jwtSettings.getTokenSigningKey());
|
||||||
|
return tokenFactory.createSafeToken(token.getToken(), jwsClaims.getBody());
|
||||||
|
} catch (UnsupportedJwtException | MalformedJwtException | IllegalArgumentException | SignatureException ex) {
|
||||||
|
throw new BadCredentialsException("Invalid JWT token: ", ex);
|
||||||
|
} catch (ExpiredJwtException expiredEx) {
|
||||||
|
Date expDateTime = expiredEx.getClaims().getExpiration();
|
||||||
|
|
||||||
|
if (expDateTime == null) {
|
||||||
|
throw new BadCredentialsException("Expiry time is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime expirationTime = new DateTime(expiredEx.getClaims().getExpiration());
|
||||||
|
DateTime currentTime = DateTime.now();
|
||||||
|
|
||||||
|
if (Minutes.minutesBetween(currentTime, expirationTime).isGreaterThan(Minutes.minutes(jwtSettings.getTokenValidationTimeframe()))) {
|
||||||
|
throw new JwtExpiredTokenException(token, "JWT token has expired", expiredEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return refreshToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SafeJwtToken refreshToken() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package com.svlada.security.auth.jwt;
|
package com.svlada.security.auth.jwt;
|
||||||
|
|
||||||
import com.svlada.security.model.SafeJwtToken;
|
import com.svlada.security.model.SafeJwtToken;
|
||||||
|
import com.svlada.security.model.UnsafeJwtToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -9,5 +10,5 @@ import com.svlada.security.model.SafeJwtToken;
|
|||||||
* Aug 5, 2016
|
* Aug 5, 2016
|
||||||
*/
|
*/
|
||||||
public interface TokenAuthStrategy {
|
public interface TokenAuthStrategy {
|
||||||
public SafeJwtToken authenticate(String token);
|
public SafeJwtToken authenticate(UnsafeJwtToken token);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,19 @@ public class JwtSettings {
|
|||||||
*/
|
*/
|
||||||
private String tokenSigningKey;
|
private String tokenSigningKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link JwtToken} can be refreshed during this timeframe.
|
||||||
|
*/
|
||||||
|
private Integer tokenValidationTimeframe;
|
||||||
|
|
||||||
|
public Integer getTokenValidationTimeframe() {
|
||||||
|
return tokenValidationTimeframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTokenValidationTimeframe(Integer tokenValidationTimeframe) {
|
||||||
|
this.tokenValidationTimeframe = tokenValidationTimeframe;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getTokenExpirationTime() {
|
public Integer getTokenExpirationTime() {
|
||||||
return tokenExpirationTime;
|
return tokenExpirationTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import com.svlada.security.config.JwtSettings;
|
|||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import io.jsonwebtoken.impl.DefaultClaims;
|
||||||
import io.jsonwebtoken.lang.Collections;
|
import io.jsonwebtoken.lang.Collections;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,24 +40,18 @@ public class JwtTokenFactory {
|
|||||||
* @param roles
|
* @param roles
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public SafeJwtToken createSafeToken(UserContext userContext, final Collection<GrantedAuthority> roles) {
|
public SafeJwtToken createSafeToken(UserContext userContext) {
|
||||||
if (StringUtils.isBlank(userContext.getUsername())) {
|
if (StringUtils.isBlank(userContext.getUsername())) {
|
||||||
throw new IllegalArgumentException("Cannot create JWT Token without username");
|
throw new IllegalArgumentException("Cannot create JWT Token without username");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Collections.isEmpty(roles)) {
|
|
||||||
throw new IllegalArgumentException("Cannot create JWT Token without roles");
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTime currentTime = new DateTime();
|
DateTime currentTime = new DateTime();
|
||||||
|
|
||||||
Claims claims = Jwts.claims();
|
Claims claims = Jwts.claims().setSubject(userContext.getUsername());
|
||||||
claims.put("roles", AuthorityUtils.authorityListToSet(roles));
|
|
||||||
|
|
||||||
String token = Jwts.builder()
|
String token = Jwts.builder()
|
||||||
.setClaims(claims)
|
.setClaims(claims)
|
||||||
.setIssuer(settings.getTokenIssuer())
|
.setIssuer(settings.getTokenIssuer())
|
||||||
.setSubject(userContext.getUsername())
|
|
||||||
.setIssuedAt(currentTime.toDate())
|
.setIssuedAt(currentTime.toDate())
|
||||||
.setExpiration(currentTime.plusMinutes(settings.getTokenExpirationTime()).toDate())
|
.setExpiration(currentTime.plusMinutes(settings.getTokenExpirationTime()).toDate())
|
||||||
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
|
.signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey())
|
||||||
@ -64,6 +59,11 @@ public class JwtTokenFactory {
|
|||||||
|
|
||||||
return new SafeJwtToken(token, claims);
|
return new SafeJwtToken(token, claims);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SafeJwtToken createSafeToken(String token, Claims claims) {
|
||||||
|
return new SafeJwtToken(token, claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unsafe version of JWT token is created.
|
* Unsafe version of JWT token is created.
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
package com.svlada.security.model;
|
package com.svlada.security.model;
|
||||||
|
|
||||||
import com.svlada.security.auth.jwt.TokenAuthStrategy;
|
|
||||||
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jws;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
|
|
||||||
public class UnsafeJwtToken implements JwtToken {
|
public class UnsafeJwtToken implements JwtToken {
|
||||||
@ -16,24 +15,10 @@ public class UnsafeJwtToken implements JwtToken {
|
|||||||
* Validates JWT Token signature.
|
* Validates JWT Token signature.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void validateToken(String signingKey) {
|
public Jws<Claims> parse(String signingKey) {
|
||||||
Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token);
|
return Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract Claims object from the rawToken.
|
|
||||||
*
|
|
||||||
* @param signingKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Claims parseClaims(String signingKey) {
|
|
||||||
return Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token).getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SafeJwtToken authenticate(TokenAuthStrategy strategy) {
|
|
||||||
return strategy.authenticate(this.token);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getToken() {
|
public String getToken() {
|
||||||
return token;
|
return token;
|
||||||
|
|||||||
@ -12,12 +12,10 @@ import org.springframework.security.core.GrantedAuthority;
|
|||||||
*/
|
*/
|
||||||
public class UserContext {
|
public class UserContext {
|
||||||
private final String username;
|
private final String username;
|
||||||
private final String email;
|
|
||||||
private final List<GrantedAuthority> authorities;
|
private final List<GrantedAuthority> authorities;
|
||||||
|
|
||||||
public UserContext(String username, String email, List<GrantedAuthority> authorities) {
|
public UserContext(String username, List<GrantedAuthority> authorities) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.email = email;
|
|
||||||
this.authorities = authorities;
|
this.authorities = authorities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,10 +23,6 @@ public class UserContext {
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<GrantedAuthority> getAuthorities() {
|
public List<GrantedAuthority> getAuthorities() {
|
||||||
return authorities;
|
return authorities;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,12 @@ public class UserService {
|
|||||||
public UserContext loadUser(String username, String password) {
|
public UserContext loadUser(String username, String password) {
|
||||||
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
|
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
|
||||||
authorities.add(new SimpleGrantedAuthority(UserRole.ADMIN.authority()));
|
authorities.add(new SimpleGrantedAuthority(UserRole.ADMIN.authority()));
|
||||||
return new UserContext(username, "svlada@gmail.com", authorities);
|
return new UserContext(username, authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserContext loadUser(String username) {
|
||||||
|
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
|
||||||
|
authorities.add(new SimpleGrantedAuthority(UserRole.ADMIN.authority()));
|
||||||
|
return new UserContext(username, authorities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user