实现Token的方式有很多,本篇介绍的是利用Json Web Token(JWT)生成的Token.JWT生成的Token有什么好处呢?
- 安全性比较高,加上密匙加密而且支持多种算法。
- 携带的信息是自定义的,而且可以做到验证token是否过期。
- 验证信息可以由前端保存,后端不需要为保存token消耗内存。
本篇分3部分进行讲解。
- 什么是JWT
- JWT的代码实现,代码将JWT封装成两个工具类,可以直接调用。
- 总结
什么是JWT
JSON Web Token 简称JWT。
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
JWT生成的token是这样的
eyJpc3MiOiJKb2huI.eyJpc3MiOiJ.Kb2huIFd1IEp
生成的token,是3段,用.连接。下面有解释。
头部
用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。
例如:
{
"typ": "JWT",
"alg": "HS256"
}
载荷
其实就是自定义的数据,一般存储用户Id,过期时间等信息。也就是JWT的核心所在,因为这些数据就是使后端知道此token是哪个用户已经登录的凭证。而且这些数据是存在token里面的,由前端携带,所以后端几乎不需要保存任何数据。
例如:
{
"uid": "xxxxidid", //用户id
"exp": "12121212" //过期时间
}
签名
签名其实就是:
- 头部和载荷各自base64加密后用.连接起来,然后就形成了xxx.xx的前两段token。
- 最后一段token的形成是,前两段加入一个密匙用HS256算法或者其他算法加密形成。
- 所以token3段的形成就是在签名处形成的。
代码实现
- 看代码前一定要知道JWT是由头部、载荷与签名组成。
- 代码将JWT封装成两个工具类,可以直接调用。
需要下载的jar包
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
JWT工具类
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.internal.com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class JWT {
private static final String SECRET = "XX#$%()(#*!()!KL<><MQLMNQNQJQK sdfkjsdrow32234545fdf>?N<:{LWPW";
private static final String EXP = "exp";
private static final String PAYLOAD = "payload";
//加密,传入一个对象和有效期
public static <T> String sign(T object, long maxAge) {
try {
final JWTSigner signer = new JWTSigner(SECRET);
final Map<String, Object> claims = new HashMap<String, Object>();
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(object);
claims.put(PAYLOAD, jsonString);
claims.put(EXP, System.currentTimeMillis() + maxAge);
return signer.sign(claims);
} catch(Exception e) {
return null;
}
}
//解密,传入一个加密后的token字符串和解密后的类型
public static<T> T unsign(String jwt, Class<T> classT) {
final JWTVerifier verifier = new JWTVerifier(SECRET);
try {
final Map<String,Object> claims= verifier.verify(jwt);
if (claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
long exp = (Long)claims.get(EXP);
long currentTimeMillis = System.currentTimeMillis();
if (exp > currentTimeMillis) {
String json = (String)claims.get(PAYLOAD);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, classT);
}
}
return null;
} catch (Exception e) {
return null;
}
}
}
WebConfigurer类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.hc.interceptor.TokenInterceptor;
@Configuration
public class WebConfigurer extends WebMvcConfigurationSupport {
@Autowired
private TokenInterceptor tokenInterceptor;
// 这个方法是用来配置静态资源的,比如html,js,css,等等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
// 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns("/**") 表示拦截所有的请求,
// excludePathPatterns("/login", "/register") 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
registry.addInterceptor(tokenInterceptor).addPathPatterns("/**").excludePathPatterns("/login/**","/Typhoon/**","/Nephogram/**","/Ocean/**","/Siteinfo/**","/Siteinfo/**", "/User/**");
super.addInterceptors(registry);
}
}
设置token拦截器
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSONObject;
import com.hc.config.JWT;
import com.hc.pojo.User;
import com.hc.util.ResponseData;
@Component
public class TokenInterceptor implements HandlerInterceptor{
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception arg3)
throws Exception {
}
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView model) throws Exception {
}
//拦截每个请求
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
response.setCharacterEncoding("utf-8");
String token =request.getHeader("Authorization");
ResponseData responseData = ResponseData.ok();
//token不存在
if(null != token) {
User user = JWT.unsign(token, User.class);
if(null != user) {
return true;
}else{
responseData = ResponseData.tokenOverdue();
responseMessage(response, response.getWriter(), responseData);
return false;
}
}
else
{
responseData = ResponseData.forbidden();
responseMessage(response, response.getWriter(), responseData);
return false;
}
}
//请求不通过,返回错误信息给客户端
private void responseMessage(HttpServletResponse response, PrintWriter out, ResponseData responseData) {
response.setContentType("application/json; charset=utf-8");
String json = JSONObject.toJSONString(responseData);
out.print(json);
out.flush();
out.close();
}
}
调用业务逻辑
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.hc.config.JWT;
import com.hc.pojo.User;
import com.hc.service.UserService;
import com.hc.util.ResponseData;
@Controller
@RequestMapping(value = "/User", produces = MediaType.APPLICATION_JSON_VALUE)
@CrossOrigin(origins="*")
public class UserController {
@Autowired
private UserService userService;
private static final Integer rolename = 1;//管理员权限
/**
* 权限判断
* */
public boolean token(String token) {
User user = JWT.unsign(token, User.class);
if(user.getRole()==rolename) {
return true;
}else {
return false;
}
}
/**
* 用户添加
* */
@RequestMapping("addUser")
@ResponseBody
public Object addUser(User user,@RequestHeader("Authorization") String token) {
if(token(token)) {
Integer num = userService.addUser(user);
if(num > 0) {
return user.getId();
}else {
return ResponseData.serverInternalError();
}
}else {
return ResponseData.unauthorized();
}
}