如何在SpringBoot项目中使用JWT实现接口鉴权功能

一、简介JWT:

     JWT由Header(请求头)、Payload(携带的用户信息)、Signature(加密后生成的签名)三部分组成,也就是我们用过JWT所眼熟的header.payload.signature。JWT可以自定义Signing Key,能够有效的防止伪造或篡改token签名。

 

二、思考:

    为什么要接口鉴权?怎样的业务场景下需要使用鉴权功能?

    1:客户端登录后可操作内容鉴权:打个比方,评论是很常见的业务功能,那么在评论之前客户端需校验用户是否登录,这个时候我们就可以通过使用jwt产生的token来判断用户的登录状态,登录后才会返回token,客户端请求,服务端校验。

    2:登录状态过期校验:当然有了登录过后也得有登录过期,JWT可以很简便的设置token expire时间,服务端配置每次都自动校验token是否过期,如果过期就直接抛出异常,客户端需要重新登录申请token。

    3:Restful Api 的身份认证:业务接口必须要使用身份验证才允许访问。

三、实践

1. 导入jwt的maven依赖

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.7.0</version>
</dependency>

  2. 新建JwtHelper类

  生定义生成sign key:

public static SecretKey generalKey(){
        byte[] encodedKey = Base64.decodeBase64("francis1q2we3");//自定义
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

创建jwt  ,claim为自定义加密参数(可以吧用户id 或者用户昵称加入里面)。

/**
 * 创建JWT
 */
public static String createJWT(String name, String userId,
			long TTLMillis) {
	SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

	long nowMillis = System.currentTimeMillis();
	Date now = new Date(nowMillis);
	// 添加构成JWT的参数
	JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
			.claim("nickName", name)
			.claim("memId", userId)
			.setIssuer("francis")//发行者,自定义
                        .setAudience("client")
			.signWith(signatureAlgorithm, generalKey());
	// 添加Token过期时间
	if (TTLMillis >= 0) {
		long expMillis = nowMillis + TTLMillis;
		Date exp = new Date(expMillis);
		builder.setExpiration(exp).setNotBefore(now);
	}
	// 生成JWT
	return builder.compact();
}

  解析token:

public static Claims parseJWT(String jsonWebToken) {
	try {
		Claims claims = Jwts.parser().setSigningKey(generalKey())
				.parseClaimsJws(jsonWebToken).getBody();
		return claims;
	} catch (Exception ex) {
		System.out.println(ex);
		return null;
	}
}

 从解析的JWT中获取参与加密的claims

public static Integer getUserId(String jsonWebToken){
	jsonWebToken = jsonWebToken.substring(7, jsonWebToken.length());
	Claims claims = parseJWT(jsonWebToken);
	return Integer.parseInt(claims.get("memId").toString());
}

public static String getNickName(String jsonWebToken){
	jsonWebToken = jsonWebToken.substring(7, jsonWebToken.length());
	Claims claims = parseJWT(jsonWebToken);
	return Integer.parseInt(claims.get("nickName").toString());
}

3. 添加JwtFilter过滤器检验token

package com.francis.api.config.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.GenericFilterBean;

import com.feilong.core.Validator;
import com.xj.api.util.JsonUtil;
import com.xj.api.util.JwtHelper;
import com.xj.common.base.common.bean.Result;
import com.xj.common.base.common.exception.EnumSvrResult;
import com.xj.common.bussiness.member.entity.QMember;

public class JwtFilter extends GenericFilterBean {
	
	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;
		if("OPTIONS".equals(request.getMethod())){//预请求直接通行
			chain.doFilter(request, response);
			return;
		}
		String auth = request.getHeader("token");
		if ((auth != null) && (auth.length() > 7)) {
			String headStr = auth.substring(0, 6).toLowerCase();
			if (headStr.compareTo("bearer") == 0) { //按照国际惯例,客户端在发送token的时候需要告知来人,所以在token前面加上的“bearer;”,这里下步进行token校验时需要分离出来。
				auth = auth.substring(7, auth.length());
				System.out.println(auth);
				if (JwtHelper.parseJWT(auth) != null) {
					chain.doFilter(request, response);
					return;
				}
			}
		}
		response.setCharacterEncoding("UTF-8");    
		response.setContentType("application/json; charset=utf-8");   
		response.setStatus(HttpServletResponse.SC_OK);  
		response.getWriter().write(JsonUtil.getJsonFromObject(new Result(EnumSvrResult.ERROR_TOKEN)));  //自定义的token无效或不存在的返回异常
        return;  
	}
	
}

4. JwtConfig 用来定义需过滤那些接口。本文定义过滤后缀为“.auth”的接口。

package com.francis.api.config.jwt;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration  
public class JwtConfig {
	
 	@Bean  
    public FilterRegistrationBean basicFilterRegistrationBean(){  
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();  
        registrationBean.setFilter(new JwtFilter);  
          
        List<String> urlPatterns = new ArrayList<>();  
        urlPatterns.add("*.auth");  
        registrationBean.setUrlPatterns(urlPatterns);  
        return registrationBean;  
    } 
 	
}

5. 获取token,登录成功后调用createJWT方法传入对应参数及失效时间(毫秒),即可生成token返回给客户端。

6. 接口路由定义事例:@GetMapping("/getUserInfo/{id}.auth");所有后缀是.auth的接口都会走filter校验流程。

7. 前端如何传递token? 前端获取到token 后只需要在接口请求header中加入token属性,值为' bearer;+ token '

四、尾声

JWT的使用在此告一段落,总体来说操作并不复杂,生成、解析token的方法也很简单。

友情提示: JWT虽好,可不要太放心哦,相对可能存在token 被盗用的风险,所以尽量减少使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

 

 


正常业务逻辑中,我们需要一个用户只能在一个地方登录,也就是说在登录状态未失效的情况下,下一次登录必须踢掉上一次登录。然而JWT并没有实现手动token 过期功能,那么我们在项目中要如何实现这一功能呢?

请听下回分解!>>>紧戳这里,直接下回分解!!!

 

 

----我是francis, 谨以此记录自己精彩的程序人生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值