Commit 4e9d74bc by stylefeng

完善filter token校验

parent d432240e
...@@ -25,10 +25,6 @@ ...@@ -25,10 +25,6 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mobile</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
......
package com.stylefeng.guns.rest.common.aop; package com.stylefeng.guns.rest.common.aop;
import com.stylefeng.guns.core.aop.BaseControllerExceptionHandler; import com.stylefeng.guns.core.aop.BaseControllerExceptionHandler;
import com.stylefeng.guns.core.base.tips.ErrorTip;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import io.jsonwebtoken.JwtException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/** /**
* 全局的的异常拦截器(拦截所有的控制器)(带有@RequestMapping注解的方法上都会拦截) * 全局的的异常拦截器(拦截所有的控制器)(带有@RequestMapping注解的方法上都会拦截)
...@@ -16,4 +23,13 @@ public class GlobalExceptionHandler extends BaseControllerExceptionHandler { ...@@ -16,4 +23,13 @@ public class GlobalExceptionHandler extends BaseControllerExceptionHandler {
private Logger log = LoggerFactory.getLogger(this.getClass()); private Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 拦截jwt相关异常
*/
@ExceptionHandler(JwtException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorTip jwtException(JwtException e) {
return new ErrorTip(BizExceptionEnum.AUTH_ERROR.getCode(), BizExceptionEnum.AUTH_ERROR.getMessage());
}
} }
...@@ -8,8 +8,9 @@ package com.stylefeng.guns.rest.common.exception; ...@@ -8,8 +8,9 @@ package com.stylefeng.guns.rest.common.exception;
*/ */
public enum BizExceptionEnum { public enum BizExceptionEnum {
AUTH_REQUEST_ERROR(701, "auth请求验证失败"), AUTH_REQUEST_ERROR(700, "auth请求验证失败"),
AUTH_ERROR(702, "签名错误,请求失败"), TOKEN_EXPIRED(700, "token 过期"),
AUTH_ERROR(700, "签名错误,请求失败"),
SERVER_ERROR(802, "服务器错误"); SERVER_ERROR(802, "服务器错误");
BizExceptionEnum(int code, String message) { BizExceptionEnum(int code, String message) {
......
...@@ -7,7 +7,6 @@ import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil; ...@@ -7,7 +7,6 @@ import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator; import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.mobile.device.Device;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -32,13 +31,13 @@ public class AuthController { ...@@ -32,13 +31,13 @@ public class AuthController {
private IReqValidator reqValidator; private IReqValidator reqValidator;
@RequestMapping(value = "${jwt.auth-path}") @RequestMapping(value = "${jwt.auth-path}")
public ResponseEntity<?> createAuthenticationToken(@RequestParam Map<String, Object> params, Device device, HttpServletRequest request) { public ResponseEntity<?> createAuthenticationToken(@RequestParam Map<String, Object> params, HttpServletRequest request) {
boolean validate = reqValidator.validate(params); boolean validate = reqValidator.validate(params);
if (validate) { if (validate) {
final String token = jwtTokenUtil.generateToken((String) params.get("userName"), device);
final String randomKey = jwtTokenUtil.getRandomKey(); final String randomKey = jwtTokenUtil.getRandomKey();
final String token = jwtTokenUtil.generateToken((String) params.get("userName"), randomKey);
return ResponseEntity.ok(new AuthResponse(token, randomKey)); return ResponseEntity.ok(new AuthResponse(token, randomKey));
} else { } else {
throw new BussinessException(BizExceptionEnum.AUTH_REQUEST_ERROR); throw new BussinessException(BizExceptionEnum.AUTH_REQUEST_ERROR);
......
package com.stylefeng.guns.rest.modular.auth.filter; package com.stylefeng.guns.rest.modular.auth.filter;
import com.stylefeng.guns.core.base.tips.ErrorTip;
import com.stylefeng.guns.core.util.RenderUtil;
import com.stylefeng.guns.rest.common.exception.BizExceptionEnum; import com.stylefeng.guns.rest.common.exception.BizExceptionEnum;
import com.stylefeng.guns.rest.common.exception.BussinessException;
import com.stylefeng.guns.rest.config.properties.JwtProperties; import com.stylefeng.guns.rest.config.properties.JwtProperties;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil; import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import io.jsonwebtoken.JwtException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -41,14 +43,23 @@ public class AuthFilter extends OncePerRequestFilter { ...@@ -41,14 +43,23 @@ public class AuthFilter extends OncePerRequestFilter {
String authToken = null; String authToken = null;
if (requestHeader != null && requestHeader.startsWith("Bearer ")) { if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7); authToken = requestHeader.substring(7);
boolean flag = jwtTokenUtil.validateToken(authToken);
if (!flag) { //验证token是否过期
logger.error("token验证错误"); try {
throw new BussinessException(BizExceptionEnum.AUTH_ERROR); boolean flag = jwtTokenUtil.isTokenExpired(authToken);
if (flag) {
RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
return;
}
} catch (JwtException e) {
//有异常就是token解析失败
RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.AUTH_ERROR.getCode(), BizExceptionEnum.AUTH_ERROR.getMessage()));
return;
} }
} else { } else {
logger.warn("错误的header"); //header没有带Bearer字段
throw new BussinessException(BizExceptionEnum.AUTH_ERROR); RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.AUTH_ERROR.getCode(), BizExceptionEnum.AUTH_ERROR.getMessage()));
return;
} }
chain.doFilter(request, response); chain.doFilter(request, response);
} }
......
...@@ -7,146 +7,126 @@ import io.jsonwebtoken.JwtException; ...@@ -7,146 +7,126 @@ import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mobile.device.Device;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
/**
* <p>jwt token工具类</p>
* <pre>
* jwt的claim里一般包含以下几种数据:
* 1. iss -- token的发行者
* 2. sub -- 该JWT所面向的用户
* 3. aud -- 接收该JWT的一方
* 4. exp -- token的失效时间
* 5. nbf -- 在此时间段之前,不会被处理
* 6. iat -- jwt发布时间
* 7. jti -- jwt唯一标识,防止重复使用
* </pre>
*
* @author fengshuonan
* @Date 2017/8/25 10:59
*/
@Component @Component
public class JwtTokenUtil implements Serializable { public class JwtTokenUtil {
@Autowired @Autowired
private JwtProperties jwtProperties; private JwtProperties jwtProperties;
private static final long serialVersionUID = -3301605591108950415L; /**
* 获取用户名从token中
static final String CLAIM_KEY_USERNAME = "sub"; */
static final String CLAIM_KEY_AUDIENCE = "aud";
static final String CLAIM_KEY_CREATED = "iat";
static final String AUDIENCE_UNKNOWN = "unknown";
static final String AUDIENCE_WEB = "web";
static final String AUDIENCE_MOBILE = "mobile";
static final String AUDIENCE_TABLET = "tablet";
public String getUsernameFromToken(String token) { public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject); return getClaimFromToken(token).getSubject();
} }
/**
* 获取jwt发布时间
*/
public Date getIssuedAtDateFromToken(String token) { public Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token, Claims::getIssuedAt); return getClaimFromToken(token).getIssuedAt();
} }
/**
* 获取jwt失效时间
*/
public Date getExpirationDateFromToken(String token) { public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration); return getClaimFromToken(token).getExpiration();
} }
/**
* 获取jwt接收者
*/
public String getAudienceFromToken(String token) { public String getAudienceFromToken(String token) {
return getClaimFromToken(token, Claims::getAudience); return getClaimFromToken(token).getAudience();
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
} }
public Boolean validateToken(String token) { /**
try { * 获取私有的jwt claim
//判断是否能解析出token */
Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody(); public String getPrivateClaimFromToken(String token, String key) {
return true; return getClaimFromToken(token).get(key).toString();
} catch (JwtException e) {
return false;
}
} }
private Claims getAllClaimsFromToken(String token) { /**
* 获取jwt的payload部分
*/
public Claims getClaimFromToken(String token) {
return Jwts.parser() return Jwts.parser()
.setSigningKey(jwtProperties.getSecret()) .setSigningKey(jwtProperties.getSecret())
.parseClaimsJws(token) .parseClaimsJws(token)
.getBody(); .getBody();
} }
private Boolean isTokenExpired(String token) { /**
final Date expiration = getExpirationDateFromToken(token); * 解析token是否正确,不正确会报异常<br>
return expiration.before(new Date()); */
} public void parseToken(String token) throws JwtException {
Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody();
private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
return (lastPasswordReset != null && created.before(lastPasswordReset));
}
private String generateAudience(Device device) {
String audience = AUDIENCE_UNKNOWN;
if (device.isNormal()) {
audience = AUDIENCE_WEB;
} else if (device.isTablet()) {
audience = AUDIENCE_TABLET;
} else if (device.isMobile()) {
audience = AUDIENCE_MOBILE;
}
return audience;
} }
private Boolean ignoreTokenExpiration(String token) { /**
String audience = getAudienceFromToken(token); * <pre>
return (AUDIENCE_TABLET.equals(audience) || AUDIENCE_MOBILE.equals(audience)); * 验证token是否失效
* true:过期 false:没过期
* </pre>
*/
public Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
} }
public String generateToken(String userName, Device device) { /**
* 生成token(通过用户名和签名时候用的随机数)
*/
public String generateToken(String userName, String randomKey) {
Map<String, Object> claims = new HashMap<>(); Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userName, generateAudience(device)); claims.put("randomKey", randomKey);
return doGenerateToken(claims, userName, randomKey);
} }
private String doGenerateToken(Map<String, Object> claims, String subject, String audience) { /**
* 生成token
*/
private String doGenerateToken(Map<String, Object> claims, String subject, String randomKey) {
final Date createdDate = new Date(); final Date createdDate = new Date();
final Date expirationDate = new Date(createdDate.getTime() + jwtProperties.getExpiration() * 1000); final Date expirationDate = new Date(createdDate.getTime() + jwtProperties.getExpiration() * 1000);
System.out.println("doGenerateToken " + createdDate);
return Jwts.builder() return Jwts.builder()
.setClaims(claims) .setClaims(claims)
.setSubject(subject) .setSubject(subject)
.setAudience(audience)
.setIssuedAt(createdDate) .setIssuedAt(createdDate)
.setExpiration(expirationDate) .setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret()) .signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
.compact(); .compact();
} }
public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) { /**
final Date created = getIssuedAtDateFromToken(token); * 获取混淆MD5签名用的随机字符串
return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) */
&& (!isTokenExpired(token) || ignoreTokenExpiration(token)); public String getRandomKey() {
}
public String refreshToken(String token) {
final Claims claims = getAllClaimsFromToken(token);
claims.setIssuedAt(new Date());
return doRefreshToken(claims);
}
public String doRefreshToken(Claims claims) {
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
.compact();
}
public Boolean validateToken(String token, String userName) {
final String username = getUsernameFromToken(token);
final Date created = getIssuedAtDateFromToken(token);
//final Date expiration = getExpirationDateFromToken(token);
return (
username.equals(userName)
&& !isTokenExpired(token));
}
public String getRandomKey(){
return ToolUtil.getRandomString(6); return ToolUtil.getRandomString(6);
} }
} }
\ No newline at end of file
...@@ -14,7 +14,7 @@ public class DecryptTest { ...@@ -14,7 +14,7 @@ public class DecryptTest {
String key = "mySecret"; String key = "mySecret";
String compactJws = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImF1ZCI6IndlYiIsImV4cCI6MTUwNDA3NDg1OCwiaWF0IjoxNTAzNDcwMDU4fQ.S6eOFPkDl77sg9mwhgkQZ37504OaYFTZs4pMb2FuG-UKbimTsiOAU_gQhqfOfBOIho8Ab4C1fnIN-4z-Ie8V8g"; String compactJws = "eyJhbGciOiJIUzUxMiJ9.eyJyYW5kb21LZXkiOiJjdXVuYXgiLCJzdWIiOiJhZG1pbiIsImV4cCI6MTUwNDIzNjk1MywiaWF0IjoxNTAzNjMyMTUzfQ.8Feb8wU67zVOGuxWEllH8I_q5VKVsIHAdeJKTRLkmrhvfPzd0Xzx5C_DRvctTpzXjEw5v3czNTJyzai1Q1LpJg";
System.out.println("body = " + Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getBody()); System.out.println("body = " + Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getBody());
System.out.println("header = " + Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getHeader()); System.out.println("header = " + Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getHeader());
......
...@@ -22,7 +22,7 @@ public class JWTTest { ...@@ -22,7 +22,7 @@ public class JWTTest {
String compactJws = Jwts.builder() String compactJws = Jwts.builder()
.setSubject("Joe") .setSubject("Joe")
.setClaims(new DefaultClaims().setId(IdGenerator.getId())) .setClaims(new DefaultClaims().setId(IdGenerator.getId()))
.signWith(SignatureAlgorithm.HS512, key) .signWith(SignatureAlgorithm.RS512, key)
.compact(); .compact();
System.out.println(compactJws); System.out.println(compactJws);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment