理解使用Token
Token的组成
token三大组成:
1、header:头部信息
2、payload:存放自己想要的信息
3、sign:是为了防止恶意篡改数据
JWT的简单介绍
1、什么是jwt:
java web token (jwt)是目前最流行的跨域身份验证解决方案
在没有跨域和没有分布式的情况下,基本验证方式是通过将session_id存入cookie中,这样每次访问将session_id带入。
还有一种方式是将session持久化,简单的说就是将session存到数据库中,这样的不好处就是持久层坏掉的话,整个认证系统都会死掉,但是现在很多项目用到了这个方式,将session存在redis中。
当然,我们次按在重点研究jwt,他的原理就是会话数据只保存在客户端,每次请求都会被发送到服务器,
2、jwt的原则
现在服务端身份验证之后,生成一个验证数据传给用户,之后,当用户与服务器通信时,客户在请求中发回验证数据。
3、jwt的数据结构
验证数据是一个很长的字符串,他就是有我们之前说过的那三部分组成,header,负载和签名。
其中JWT头是一个源数据的json对象,如:
```java
{
"alg": "HS256",
"typ": "JWT"
}
``
alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。
有效负载也是一个json串。JWT指定七个默认字段供选择。
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
除以上默认字段外,我们还可以自定义私有字段,如下例:
{
"sub": "1234567890",
"name": "chongchong",
"admin": true
}
签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。
JWT的使用
jwt有很多jar包来让自己使用,不过还是自己写一个适合自己的最好不过了
现在我们使用springboot来 实现jwt
1、maven
redis.clients
jedis
3.0.0
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
</dependencies>
2、写一个自定义纾解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {
}
3、WebAppconfiguration
@Configuration
public class WebAppConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokemInterceptor()).addPathPatterns("/**");
}
}
4、创建拦截器
现在就是核心实现点了
```java
public class TokemInterceptor implements HandlerInterceptor {
Logger log = LoggerFactory.getLogger(TokemInterceptor.class);
//存放鉴权信息的Header名称,默认是Authorization
private String Authorization = "Authorization";
//鉴权失败后返回的HTTP错误码,默认为401
private int unauthorizedErrorCode = HttpServletResponse.SC_UNAUTHORIZED;
/**
* 存放用户名称和对应的key
*/
public static final String USER_KEY = "USER_KEY";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
//验证token
if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {
String token = request.getParameter(Authorization);
log.info("获取到的token为: {} ", token);
//此处主要设置你的redis的ip和端口号,我的redis是在本地
Jedis jedis = new Jedis("127.0.0.1", 6379);
String username = null;
if (token != null && token.length() != 0) {
//从redis中根据键token来获取绑定的username
username = jedis.get(token);
log.info("从redis中获取的用户名称为: {}", username);
}
//判断username不为空的时候
if (username != null && !username.trim().equals("")) {
String startBirthTime = jedis.get(token + username);
log.info("生成token的时间为: {}", startBirthTime);
Long time = System.currentTimeMillis() - Long.valueOf(startBirthTime);
log.info("token存在时间为 : {} ms", time);
//重新设置Redis中的token过期时间
if (time > CommonUtil.TOKEN_RESET_TIME) {
jedis.expire(username, CommonUtil.TOKEN_EXPIRE_TIME);
jedis.expire(token, CommonUtil.TOKEN_EXPIRE_TIME);
log.info("重置成功!");
Long newBirthTime = System.currentTimeMillis();
jedis.set(token + username, newBirthTime.toString());
}
//关闭资源
jedis.close();
request.setAttribute(USER_KEY, username);
return true;
} else {
JSONObject jsonObject = new JSONObject();
PrintWriter out = null;
try {
response.setStatus(unauthorizedErrorCode);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
jsonObject.put("code", ((HttpServletResponse) response).getStatus());
//鉴权失败后返回的错误信息,默认为401 unauthorized
jsonObject.put("message", HttpStatus.UNAUTHORIZED);
out = response.getWriter();
out.println(jsonObject);
return false;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != out) {
out.flush();
out.close();
}
}
}
}
request.setAttribute(USER_KEY, null);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
最后,在登录的时候添加token信息,在其他接口添加Token注解
下次我们实战一个非常棒的案例。