Commit 4e9d74bc by stylefeng

完善filter token校验

parent d432240e
......@@ -25,10 +25,6 @@
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mobile</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
......
package com.stylefeng.guns.rest.common.aop;
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.LoggerFactory;
import org.springframework.http.HttpStatus;
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注解的方法上都会拦截)
......@@ -16,4 +23,13 @@ public class GlobalExceptionHandler extends BaseControllerExceptionHandler {
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;
*/
public enum BizExceptionEnum {
AUTH_REQUEST_ERROR(701, "auth请求验证失败"),
AUTH_ERROR(702, "签名错误,请求失败"),
AUTH_REQUEST_ERROR(700, "auth请求验证失败"),
TOKEN_EXPIRED(700, "token 过期"),
AUTH_ERROR(700, "签名错误,请求失败"),
SERVER_ERROR(802, "服务器错误");
BizExceptionEnum(int code, String message) {
......
......@@ -7,7 +7,6 @@ import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import com.stylefeng.guns.rest.modular.auth.validator.IReqValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.mobile.device.Device;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
......@@ -32,13 +31,13 @@ public class AuthController {
private IReqValidator reqValidator;
@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);
if (validate) {
final String token = jwtTokenUtil.generateToken((String) params.get("userName"), device);
final String randomKey = jwtTokenUtil.getRandomKey();
final String token = jwtTokenUtil.generateToken((String) params.get("userName"), randomKey);
return ResponseEntity.ok(new AuthResponse(token, randomKey));
} else {
throw new BussinessException(BizExceptionEnum.AUTH_REQUEST_ERROR);
......
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.BussinessException;
import com.stylefeng.guns.rest.config.properties.JwtProperties;
import com.stylefeng.guns.rest.modular.auth.util.JwtTokenUtil;
import io.jsonwebtoken.JwtException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -41,14 +43,23 @@ public class AuthFilter extends OncePerRequestFilter {
String authToken = null;
if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
authToken = requestHeader.substring(7);
boolean flag = jwtTokenUtil.validateToken(authToken);
if (!flag) {
logger.error("token验证错误");
throw new BussinessException(BizExceptionEnum.AUTH_ERROR);
//验证token是否过期
try {
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 {
logger.warn("错误的header");
throw new BussinessException(BizExceptionEnum.AUTH_ERROR);
//header没有带Bearer字段
RenderUtil.renderJson(response, new ErrorTip(BizExceptionEnum.AUTH_ERROR.getCode(), BizExceptionEnum.AUTH_ERROR.getMessage()));
return;
}
chain.doFilter(request, response);
}
......
......@@ -7,146 +7,126 @@ import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mobile.device.Device;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
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
public class JwtTokenUtil implements Serializable {
public class JwtTokenUtil {
@Autowired
private JwtProperties jwtProperties;
private static final long serialVersionUID = -3301605591108950415L;
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";
/**
* 获取用户名从token中
*/
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
return getClaimFromToken(token).getSubject();
}
/**
* 获取jwt发布时间
*/
public Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token, Claims::getIssuedAt);
return getClaimFromToken(token).getIssuedAt();
}
/**
* 获取jwt失效时间
*/
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
return getClaimFromToken(token).getExpiration();
}
/**
* 获取jwt接收者
*/
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);
/**
* 获取私有的jwt claim
*/
public String getPrivateClaimFromToken(String token, String key) {
return getClaimFromToken(token).get(key).toString();
}
public Boolean validateToken(String token) {
try {
//判断是否能解析出token
Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token).getBody();
return true;
} catch (JwtException e) {
return false;
}
}
private Claims getAllClaimsFromToken(String token) {
/**
* 获取jwt的payload部分
*/
public Claims getClaimFromToken(String token) {
return Jwts.parser()
.setSigningKey(jwtProperties.getSecret())
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
/**
* 解析token是否正确,不正确会报异常<br>
*/
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);
return (AUDIENCE_TABLET.equals(audience) || AUDIENCE_MOBILE.equals(audience));
/**
* <pre>
* 验证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<>();
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 expirationDate = new Date(createdDate.getTime() + jwtProperties.getExpiration() * 1000);
System.out.println("doGenerateToken " + createdDate);
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setAudience(audience)
.setIssuedAt(createdDate)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, jwtProperties.getSecret())
.compact();
}
public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
final Date created = getIssuedAtDateFromToken(token);
return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
&& (!isTokenExpired(token) || ignoreTokenExpiration(token));
}
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(){
/**
* 获取混淆MD5签名用的随机字符串
*/
public String getRandomKey() {
return ToolUtil.getRandomString(6);
}
}
\ No newline at end of file
......@@ -14,7 +14,7 @@ public class DecryptTest {
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("header = " + Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws).getHeader());
......
......@@ -22,7 +22,7 @@ public class JWTTest {
String compactJws = Jwts.builder()
.setSubject("Joe")
.setClaims(new DefaultClaims().setId(IdGenerator.getId()))
.signWith(SignatureAlgorithm.HS512, key)
.signWith(SignatureAlgorithm.RS512, key)
.compact();
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