本文主要是springboot 集成 JWT来实现登录权限保护.
先简单介绍一下JWT:
JSON Web Token(JWT)是一个开放的标准(RFC 7519),它定义了一个紧凑且自包含的方式,用于在各方之间以JSON对象安全地传输信息。这些信息可以通过数字签名进行验证和信任。可以使用秘密(使用HMAC算法)或使用RSA的公钥/私钥对来对JWT进行签名。
具体的JWT介绍可以查看官网:https://jwt.io/introduction/
首先附上可以快速生成springboot项目的网址: https://start.spring.io/,当然也可以通过IDEA去生成一个springboot项目。
其次是JWT官网提供的生成和解析Token 的链接: https://jwt.io/
最后附上demo的地址:https://github.com/Jacob029049/JWT,有喜欢的朋友可以去clone
项目主要思路:1.login方法来获取Token ,然后把Token加到Header上
2.分别设置两个接口,filter过滤其中一个接口。来实现JWT权限的控制
下面我贴上一些关键代码:
1.登录获取Token,此处用户名密码写在配置文件里
//login get token
@RequestMapping("/login")
public String Login(@RequestParam String username,@RequestParam String password){
if (JWT_USER.equals(username) && JWT_PASSWORD.equals(password)){
return jwtUtil.creatJwtToken();
}
return "NOK";
}
postman调用接口结果如下图:成功就返回Token
一个JWTToken 由 header,payload,signature三部分组成。
2.下面是创建和解析JWTToken的Util工具类
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
//jwt含有三部分:头部(header)、载荷(payload)、签证(signature)
/*
*(1)头部一般有两部分信息:声明类型、声明加密的算法(通常使用HMAC SHA256)
*(2)载荷该部分一般存放一些有效的信息。jwt的标准定义包含五个字段:
* -iss:该JWT的签发者
- sub: 该JWT所面向的用户
- aud: 接收该JWT的一方
- exp(expires): 什么时候过期,这里是一个Unix时间戳
- iat(issued at): 在什么时候签发的
* (3)签证(signature) JWT最后一个部分。该部分是使用了HS256加密后的数据;包含三个部分:
* */
@Service("jwtUtil")
public class JWTUtil {
private final Logger logger = Logger.getLogger(this.getClass().getName());
@Value("${com.jwt.secret}")
private String SECRET;
@Value("${com.jwt.issuer}")
private String JWT_ISSUER;
public String creatJwtToken(){
final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
final long nowMillis = System.currentTimeMillis();
//设置过期时间
final long ttlMillis = 5 * 60 * 100000;
final long expMillis = nowMillis + ttlMillis;
final Date now = new Date(nowMillis);
final Date exp = new Date(expMillis);
//Create the Signature SecretKey
final byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Base64.getEncoder().encodeToString(SECRET.getBytes()));
final Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
final Map<String, Object> headerMap = new HashMap<String, Object>();
headerMap.put("alg", "HS256");
headerMap.put("typ", "JWT");
//add JWT Parameters
final JwtBuilder builder = Jwts.builder()
.setHeaderParams(headerMap)
.setIssuedAt(now)
.setExpiration(exp)
.setIssuer(JWT_ISSUER)
.signWith(signatureAlgorithm, signingKey);
logger.info("JWT[" + builder.compact()+ "]");
return builder.compact();
}
public Claims parseJWTToken(String token)
{
Claims claims = null;
try
{
claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SECRET)).parseClaimsJws(token)
.getBody();
}
catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException
| IllegalArgumentException e)
{
logger.info("Parse JWT errror " + e.getMessage());
return null;
}
return claims;
}
}
creatJwtToken() 主要是set Header(一般都是 alg:HS256,typ:JWT);set Claims(此处可以自定义,也可以用标准定义);sign(signatureAlgorithm,secretKey )secretKey是否要用base64再次编码看实际情况
需要在pom.xml里添加maven引用:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
3.自己实现的filter类的doFilter方法:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//secretKey
String SECRET = PropertyUtil.getProperty("com.jwt.secret","12345678");
String JWT_ISSUER = PropertyUtil.getProperty("com.jwt.issuer","87654321");
final byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(Base64.getEncoder().encodeToString(SECRET.getBytes()));
//请求头上的token
String jsonTokenWeb = request.getHeader(TOKEN_HEADER);
HttpResponseBean responseWsBean = new HttpResponseBean();
//exp 、iss、sign、 validate
try {
Claims claims = Jwts.parser().setSigningKey(apiKeySecretBytes).parseClaimsJws(jsonTokenWeb)
.getBody();
String iss = Objects.isNull(claims.get("iss"))?null:String.valueOf(claims.get("iss"));
if (!JWT_ISSUER.equals(iss)){
setResponseWsBean(responseWsBean, HTTP_STATUS_ERROR, HTTP_TOKEN_MESSAGE_INVALID + "issuer is incorrect. ");
response.getWriter().write(JSONObject.fromObject(responseWsBean).toString());
}else{
//validate pass
filterChain.doFilter(request,response);
}
}catch (Exception e){
logger.info("parse jwttoken error :"+ e.getMessage());
setResponseWsBean(responseWsBean, HTTP_STATUS_ERROR, HTTP_TOKEN_MESSAGE_INVALID + e.getMessage());
response.getWriter().write(JSONObject.fromObject(responseWsBean).toString());
}
}
主要是获取header上的Token信息,然后进行解析来判断时间,签名等是否一致,当然自定义的用户权限需要自己去判断,如果有权限就放行,不行的话就返回response到接口。
4.最后接口进行验证,此处我设置的url过滤的是(/home/*)
(1) http://localhost:8081/hello/welcome 返回结果正常,无论加不加header Token
(2) http://localhost:8081/home/welcome 没有添加token情况:(此处Token添加后,如若超过exp截止时间,或者签名被他人修改等,也会返回错误)
(3) http://localhost:8081/home/welcome 添加header Token情况,并且token验证有效
如果文中有什么错误,欢迎指出,以免更多的人被误导。谢谢。