jwt

为什么使用JWT?
随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单。项目为了实现前后端分离。开始采用jwt技术,同时这样也可以减轻服务端压力,服务器无需存储存储。

了解一下JWT的组成
一个jwt实际上就是一个字符串,它由三部分组成,头部、载荷与签名,这三个部分都是json格式。

在这里插入图片描述
3部分之间用“.”号做分隔。例如eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

  1. JWT头
    JWT头
    JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。
    {
    “alg”: “HS256”,
    “typ”: “JWT”
    }
    在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。
    最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

  2. 有效载荷
    有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。
    iss:发行人
    exp:到期时间
    sub:主题
    aud:用户
    nbf:在此之前不可用
    iat:发布时间
    jti:JWT ID用于标识该JWT
    除以上默认字段外,我们还可以自定义私有字段,如下例:
    {
    “sub”: “1234567890”,
    “name”: “chongchong”,
    “admin”: true
    }
    请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。
    JSON对象也使用Base64 URL算法转换为字符串保存。

  3. 签名哈希
    签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。
    首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。
    HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret)
    在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

代码实现

  1. jwt maven依赖

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.4.0</version>
    </dependency>
    
  2. JWTUtil 用于生成token,解析token

public class JWTUtil {

	private static SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS512;

	private static Key getSecretKey(String key) {
		return new SecretKeySpec(key.getBytes(), signatureAlgorithm.getJcaName());
	}

	//生成token
	public static String createJsonWebToken(UserToken userToken, String key) {
		String token = Jwts.builder()
				.setSubject(userToken.getId().toString())
				.claim("name", userToken.getName())
				.claim("role", userToken.getRole().toString())
				.claim("orgId", userToken.getOrgId().toString())
				.claim("userName", userToken.getUserName())
				.signWith(signatureAlgorithm, getSecretKey(key)).compact();
		return token;
	}
	
	//解析token
	public static UserToken parseAndValidate(String token, String key) {
		UserToken authTokenDetails = null;
		try {
			Claims claims = Jwts.parser().setSigningKey(getSecretKey(key)).parseClaimsJws(token).getBody();
			String userId = claims.getSubject();
			String name = (String) claims.get("name");
			String roleNames = (String) claims.get("role");
			String orgId = (String) claims.get("orgId");
			authTokenDetails = new UserToken();
			authTokenDetails.setId(Long.valueOf(userId));
			authTokenDetails.setName(name);
			authTokenDetails.setRole(Role.valueOf(roleNames));
			authTokenDetails.setOrgId(Long.valueOf(orgId));
		} catch (JwtException ex) {
			log.error("TOKEN解析异常###key->{}, token->{}", key, token);
			log.error(ex.getMessage(), ex);
		}
		return authTokenDetails;
	}
}
  1. 添加使用jwt认证的filter
@AllArgsConstructor
public class JWTAuthenticationFilter extends GenericFilterBean {

    private static final String HEADER_STRING = "Authorization";// 存放Token的Header Key
    
    private String jwtKey;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (!((HttpServletRequest) request).getRequestURI().startsWith("/manage/")) {
            String token = ((HttpServletRequest) request).getHeader(HEADER_STRING);
            if (StringUtils.isBlank(token)) {
                token =  request.getParameter("token");
            }
            if (StringUtils.isNotEmpty(token)) {
                UserToken userToken = JWTUtil.parseAndValidate(token, jwtKey);
                if (userToken != null) {
                    // 得到 权限(角色)
                    List<GrantedAuthority> authorities = AuthorityUtils
                            .commaSeparatedStringToAuthorityList("ROLE_" + userToken.getRole().toString());
                    Authentication authentication = new UsernamePasswordAuthenticationToken(userToken.getId(), null,
                            authorities);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                    UserContext.setUser(userToken);
           	   }
            }
        }
        chain.doFilter(request, response);
    }
}
  1. 补充代码
    Role:
@AllArgsConstructor
@Getter
public enum Role {

	ADMIN("管理员",""),
	
	private String name;

	private String appname;
	
	private static Map<String, String> RoleMap = new HashMap<>();
	
	static {
		for (Role role : values()) {
			RoleMap.put(role.toString(), role.name);
		}
	}

	public static Map<String, String> getMap() {
		return RoleMap;
	}
}

UserToken:

@Getter
@Setter
public class UserToken {

	private Long id;
	private String name;
	private Role role;
	private LocalDate expireDate;
	private Long orgId;
	private String userName;
	private Long clazzId;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值