JSON WEB Token(JWT,读作 [/dʒɒt/]),是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。JWT用于创建访问令牌该断言权利要求中的一些数字。例如,服务器可以生成具有声明“以管理员身份登录”并将其提供给客户端的令牌。然后,客户端可以使用该令牌来证明它以管理员身份登录。令牌由一方的私钥(通常是服务器的)签名,以便双方(另一方已经通过一些合适且值得信赖的方式拥有相应的公钥)能够验证令牌是否合法。该令牌被设计为紧凑,URL-safe,自包含,和可用于特别是在web浏览器的单点登录(SSO)上下文。JWT声明通常可用于在身份提供者和服务提供者之间传递经过身份验证的用户的身份,或者业务流程所要求的任何其他类型的声明。
具体介绍见官网 https://jwt.io/introduction/
步骤:
1.发送带有用户名和密码的post请求
2.验证通过后生成jwt并将用户信息放入jwt
3.将jwt存入cookie中
4.发送带有jwt cookie的请求
5.验证签名后从jwt中获取用户信息
6.返回响应信息
代码:
springboot项目,maven构建,pom.xml文件需加入jwt依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.1.0</version>
</dependency>
/**
* 校验用户名密码、生成token并返回用户对象
*
* @param email
* @param passwd
* @return
*/
public User auth(String email, String passwd) {
if (StringUtils.isBlank(email) || StringUtils.isBlank(passwd)) {
throw new UserException(Type.USER_AUTH_FAIL, "User Auth Fail");
}
User user = new User();
user.setEmail(email);
user.setPasswd(HashUtils.encryPassword(passwd));
user.setEnable(1);
List<User> list = getUserByQuery(user);
if (!list.isEmpty()) {
User retUser = list.get(0);
onLogin(retUser);
return retUser;
}
throw new UserException(Type.USER_AUTH_FAIL, "User Auth Fail");
}
private void onLogin(User user) {
String token = JwtHelper.genToken(ImmutableMap.of("email", user.getEmail(), "name", user.getName(), "ts",
Instant.now().getEpochSecond() + ""));
renewToken(token, user.getEmail());
user.setToken(token);
}
private String renewToken(String token, String email) {
redisTemplate.opsForValue().set(email, token);
redisTemplate.expire(email, 30, TimeUnit.MINUTES);
return token;
}
public User getLoginedUserByToken(String token) {
Map<String, String> map = null;
try {
map = JwtHelper.verifyToken(token);
} catch (Exception e) {
throw new UserException(Type.USER_NOT_LOGIN, "User not login");
}
String email = map.get("email");
Long expired = redisTemplate.getExpire(email);
if (expired > 0L) {
renewToken(token, email);
User user = getUserByEmail(email);
user.setToken(token);
return user;
}
throw new UserException(Type.USER_NOT_LOGIN, "user not login");
}
public void invalidate(String token) {
Map<String, String> map = JwtHelper.verifyToken(token);
redisTemplate.delete(map.get("email"));
}
@RequestMapping("auth")
public RestResponse<User> auth(@RequestBody User user) {
User finalUser = userService.auth(user.getEmail(), user.getPasswd());
return RestResponse.success(finalUser);
}
@RequestMapping("get")
public RestResponse<User> getUser(String token) {
User finalUser = userService.getLoginedUserByToken(token);
return RestResponse.success(finalUser);
}
@RequestMapping("logout")
public RestResponse<Object> logout(String token) {
userService.invalidate(token);
return RestResponse.success();
}
JwtHelper.java
生成及校验token
package com.ljq.house.user.utils;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import org.apache.commons.lang3.time.DateUtils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.common.collect.Maps;
public class JwtHelper {
private static final String SECRET = "session_secret";
private static final String ISSUER = "ljq_user";
public static String genToken(Map<String, String> claims) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTCreator.Builder builder = JWT.create().withIssuer(ISSUER).withExpiresAt(DateUtils.addDays(new Date(),
1));
claims.forEach((k, v) -> builder.withClaim(k, v));
return builder.sign(algorithm).toString();
} catch (IllegalArgumentException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static Map<String, String> verifyToken(String token) {
Algorithm algorithm = null;
try {
algorithm = Algorithm.HMAC256(SECRET);
} catch (IllegalArgumentException | UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).build();
DecodedJWT jwt = verifier.verify(token);
Map<String, Claim> map = jwt.getClaims();
Map<String, String> resultMap = Maps.newHashMap();
map.forEach((k, v) -> resultMap.put(k, v.asString()));
return resultMap;
}
}
jwt优点:
1.安全性高,防止token被伪造和篡改
2.自包含,减少存储开销
3.跨语言,支持多种语言实现,springcloud微服务尤其适用
4.支持过期,发布者等校验
注意点:
1.消息体(payload)是可以被base64解密成明文
2.jwt不适合存放大量信息,会造成token过长
3.无法作废未过期的jwt,一旦生成就无法被修改,可以借助redis自动过期来解决
总的来说jwt是实现身份认证和鉴权服务中比较好的一种方案。