一.介绍
1、JSON Web Token,简称JWT,本质是一个token,是一种紧凑的URL安全方法,用于在网络通信的双方之间传递。
2、一般放在HTTP的headers 参数里面的authorization里面,值的前面加Bearer关键字和空格。
3、主要用于身份认证和信息交换
4、由三部分组成,用英文句点连接(.),例如:xxxxxx.yyyyyy.zzzzzz
——认证呢,有很多种,session 认证,basic auth 认证,OAuth认证,token认证等。这里只是介绍了其中基于JWT技术的token认证而已。用token认证,很好解决大型分布式横行扩展问题。
二、JWT的结构语法:https://blog.csdn.net/buyaoshuohua1/article/details/73739419/
三、模仿登录+过滤器(springboot demo)
(1)流程:
登录接口未做拦截创建token
非登录接口被过滤器拦截>>验证token是否存在,是否失效>>验证成功则返回,否则就返回
(2)实现:
1、添加依赖
io.jsonwebtoken
jjwt
0.6.0
2、TokenObject
package com.example.demo.common.bo;
import org.springframework.stereotype.Component;
@Component
public class TokenObject {
/**客户端id*/
private String clientId;
/**base64加密*/
private String base64Secret;
/**用户名*/
private String name;
/**到期时间*/
private long expiresSecond;
/**管理员名称*/
private String userName;
/**管理员id*/
private Integer aId;
/**职能*/
private String role;
/**项目名称*/
private String project;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getBase64Secret() {
return base64Secret;
}
public void setBase64Secret(String base64Secret) {
this.base64Secret = base64Secret;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getExpiresSecond() {
return expiresSecond;
}
public void setExpiresSecond(long expiresSecond) {
this.expiresSecond = expiresSecond;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getaId() {
return aId;
}
public void setaId(Integer aId) {
this.aId = aId;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
}
3、JwtHelper
package com.example.demo.common.utils;
import java.util.Date;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.common.bo.TokenObject;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.Base64Codec;
public class JwtHelper {
private static Logger logger = LoggerFactory.getLogger(JwtHelper.class);
/**
* 校验Token
* @param jwt
* @param httpRequest
* @return
*/
public static int checkToken(String jwt, HttpServletRequest httpRequest){
if (!StringUtils.isBlank(jwt)){
if (jwt.split("\\.").length==3) {
logger.info("jwt:" + jwt);
String[] split = jwt.split("\\.");
String content = split[1];//负荷
String s = Base64Codec.BASE64URL.decodeToString(content);
logger.info("s:" + s);
String sign = split[2];//签名
logger.info("sign:" + sign);
JSONObject jsonObject1 = JSONObject.parseObject(s);
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
long expiresSecond = (long) jsonObject1.get("expiresSecond");
//判断是否过期
if(now.getTime()>expiresSecond)
return 2;
TokenObject o = (TokenObject) JSONObject.toJavaObject(jsonObject1, TokenObject.class);
// if (o!=null){
// String project = o.getProject();
// // if (!StaticInfo.PROJECT.equals(project))
// return 0;
// }
String jwtByStr = createJWTByObj(o);
String s2 = jwtByStr.split("\\.")[2];
logger.info("s2:" + s2);
if (sign.equals(s2)) {
logger.info("验证成功");
return 1;
} else
return 0;
}
}
return 0;
}
/**
* 获取用户id
* @param jwt
* @return
*/
public static int getIdByJWT(String jwt){
if (!StringUtils.isBlank(jwt)) {
if (jwt.split("\\.").length == 3) {
logger.info("jwt:" + jwt);
String[] split = jwt.split("\\.");
String content = split[1];
String s = Base64Codec.BASE64URL.decodeToString(content);
JSONObject jsonObject1 = JSONObject.parseObject(s);
TokenObject o = (TokenObject) JSONObject.toJavaObject(jsonObject1, TokenObject.class);
return o.getaId();
}
}
return 0;
}
/**
* 获取用户clinetId
* @param jwt
* @return
*/
public static String getClinetIdByJWT(String jwt){
if (!StringUtils.isBlank(jwt)) {
if (jwt.split("\\.").length == 3) {
logger.info("jwt:" + jwt);
String[] split = jwt.split("\\.");
String content = split[1];
String s = Base64Codec.BASE64URL.decodeToString(content);
JSONObject jsonObject1 = JSONObject.parseObject(s);
TokenObject o = (TokenObject) JSONObject.toJavaObject(jsonObject1, TokenObject.class);
return o.getClientId();
}
}
return "";
}
/**
* 获取客户信息
* @param request
* @return
* @throws CustomException
*/
public static int getIdByRequest(HttpServletRequest request) {
int i = 0;
String auth = request.getHeader("Authorization");
if ((auth != null) && (auth.length() > 6)) {
String HeadStr = auth.substring(0, 5).toLowerCase();
if (HeadStr.compareTo("basic") == 0) {
auth = auth.substring(6, auth.length());
i = JwtHelper.getIdByJWT(auth);
}
}
if (i==0)
// throw new CustomException(ResultEnum.PERMISSION_DENIED);
return i;
return i;
}
public static String createJWTByObj(TokenObject tokenObject) {
Object jsonObject = JSONObject.toJSON(tokenObject);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
//long nowMillis = System.currentTimeMillis();
// Date now = new Date(nowMillis);
//生成签名密钥
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(tokenObject.getBase64Secret());//服务器自己定义的秘钥
SecretKeySpec signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//添加构成JWT的参数
JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setPayload(jsonObject.toString())
.signWith(signatureAlgorithm, signingKey);
//生成JWT
return builder.compact();
}
}
4、AuthUtil
public class AuthUtil {
public static String getClinetIdByAuth(HttpServletRequest request) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String auth = httpRequest.getHeader("Authorization");
auth = auth.substring(7, auth.length());
return JwtHelper.getClinetIdByJWT(auth);
}
}
5、HTTPBasicAuthorizeAttribute
@Order(value = 1)
@WebFilter(filterName = "colationFilter", urlPatterns = "/testtoken/*")
public class HTTPBasicAuthorizeAttribute implements Filter {
private Logger logger = LoggerFactory.getLogger(HTTPBasicAuthorizeAttribute.class);
@Override
public void destroy() {
logger.info("后台token过滤器,溜了溜了溜了溜了");
// 可以日志管理添加
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("后台token过滤器检测");
// 1.检测当前是否需要重新登录
// if(audience!=null){
// if (audience.getClientId().equals(StaticInfo.SIGNOUT)){
// toResponse((HttpServletResponse) response,1,(HttpServletRequest)
// request);
// return;
// }
// }
// 2.检测请求同token信息
ResultEnum resultStatusCode = checkHTTPBasicAuthorize(request);
if (resultStatusCode.equals(ResultEnum.INVALID_SINGTIMEOUT)) {// 超时
toResponse((HttpServletResponse) response, 2, (HttpServletRequest) request);
return;
} else if (resultStatusCode.equals(ResultEnum.INVALID_PERMISSION_DENIED)) {// 权限不够
toResponse((HttpServletResponse) response, 0, (HttpServletRequest) request);
return;
}
logger.info("后台token过滤器检测通过");
chain.doFilter(request, response);
}
/**
* 响应
*
* @param response
* @param i
* 类型
* @throws IOException
*/
private void toResponse(HttpServletResponse response, int i, HttpServletRequest request) throws IOException {
HttpServletResponse httpResponse = response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,DELETE,PATCH,PUT");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers",
"Origin,X-Requested-With,x-requested-with,X-Custom-Header," + "Content-Type,Accept,Authorization");
String method = request.getMethod();
if ("OPTIONS".equalsIgnoreCase(method)) {
logger.info("OPTIONS请求");
httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
}
ObjectMapper mapper = new ObjectMapper();
PrintWriter writer = httpResponse.getWriter();
// if (i==1)
// writer.write(mapper.writeValueAsString(ResultEnum.RESTARTLOGIN));
if (i == 2)
writer.write(mapper.writeValueAsString(ResultEnum.INVALID_SINGTIMEOUT));
else if (i == 0)
writer.write(mapper.writeValueAsString(ResultEnum.INVALID_PERMISSION_DENIED));
writer.close();
if (writer != null)
writer = null;
}
@Override
public void init(FilterConfig arg0) throws ServletException {
logger.info("后台token过滤器启动");
}
/**
* 检测请求同token信息
*
* @param request
* @return
*/
private ResultEnum checkHTTPBasicAuthorize(ServletRequest request) {
try {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String auth = httpRequest.getHeader("Authorization");
if ((auth != null) && (auth.length() > 7)) {
String HeadStr = auth.substring(0, 6).toLowerCase();
/*if (HeadStr.compareTo("basic") == 0) {
auth = auth.substring(6, auth.length());
int i = JwtHelper.checkToken(auth, httpRequest);
if (i == 1) {
return ResultEnum.OK;
} else if (i == 2) {
return ResultEnum.INVALID_SINGTIMEOUT;
} else if (i == 0) {
return ResultEnum.INVALID_PERMISSION_DENIED;
}
}*/
if (HeadStr.compareTo("bearer") == 0) {
auth = auth.substring(7, auth.length());
int i = JwtHelper.checkToken(auth, httpRequest);
if (i == 1) {
return ResultEnum.OK;
} else if (i == 2) {
return ResultEnum.INVALID_SINGTIMEOUT;
} else if (i == 0) {
return ResultEnum.INVALID_PERMISSION_DENIED;
}
}
}
return ResultEnum.INVALID_PERMISSION_DENIED;
} catch (Exception ex) {
return ResultEnum.INVALID_PERMISSION_DENIED;
}
}
}
6、LoginController
@Api(value = "LoginController")
@RequestMapping("/api")
@Controller
public class LoginController {
@ApiOperation(value = "登录测试方法", notes = "登录测试方法")
@RequestMapping(value = "/login", method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public ResultModel login(@RequestBody String userName) {
System.out.println("用户名:"+userName);
// 1、业务逻辑
// 2、返回token
TokenObject token = new TokenObject();
token.setBase64Secret("test");
token.setClientId(userName);
token.setExpiresSecond(new Date().getTime() + 5*60 * 1000);
return ResultModel.success(JwtHelper.createJWTByObj(token));
}
}
7、TestTokenController
@Api(value = "TestTokenController")
@RequestMapping("/testtoken")
@RestController
public class TestTokenController {
@ApiOperation(value = "token测试方法", notes = "token测试方法")
@RequestMapping(value = "/test", method = RequestMethod.POST)
public ResultModel test(String p,HttpServletRequest request) {
return ResultModel.success(AuthUtil.getClinetIdByAuth(request));
}
}
(3)效果:
登录:
用token访问业务接口: