(转载)SpringMVC 集成 JWT验证方式

JWT官网: https://jwt.io/
原文地址:

https://www.cnblogs.com/ifindu-san/p/9890879.html

这里以java的ssm框架为例,集成jwt。

1.pom.xml 导入jwt的包

<!-- jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>2.2.0</version>
</dependency>

2.编写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;
    }
}

}

3.jwt有了,ssm要如何去利用,用户验证的第一步是登录,登录时根据用户传来的username和password到数据库验证身份,如果合法,便给该用户jwt加密生成token

//处理登录
@RequestMapping(value="login", produces = "application/json; charset=utf-8")
public @ResponseBody ResponseData login(HttpServletRequest request, @RequestParam( "email") String email,
        @RequestParam("password") String password) {
    Login login = new Login();
    login.setEmail(email);
    login.setPassword(password);
    ResponseData responseData = ResponseData.ok();
    //先到数据库验证
    Integer loginId = userService.checkLogin(login);
    if(null != loginId) {
        User user = userService.getUserByLoginId(loginId);
        login.setId(loginId);
        //给用户jwt加密生成token
        String token = JWT.sign(login, 60L* 1000L* 30L);
        //封装成对象返回给客户端
        responseData.putDataValue("loginId", login.getId());
        responseData.putDataValue("token", token);
        responseData.putDataValue("user", user);
    }
    else{
        responseData =  ResponseData.customerError();
    }   
    return responseData;
}

4.在用户登录时,把loginId和token返回给前台,以后用户每次请求时,都得带上这两个参数,后台拿到token后解密出loginId,与用户传递过来的loginId比较,如果相同,则说明用户身份合法。因为是每个登录过后的每个请求,这里用springmvc的拦截器做

<mvc:interceptors>    
    <mvc:interceptor>    
        <!-- 匹配的是url路径, 如果不配置或/**,将拦截所有的Controller -->  
        <mvc:mapping path="/**" />  
        <!-- /register 和 /login 不需要拦截-->  
        <mvc:exclude-mapping path="/register" />
        <mvc:exclude-mapping path="/login" />
        <bean class="com.xforce.charles.interceptor.TokenInterceptor"></bean>    
    </mvc:interceptor>  
    <!-- 当设置多个拦截器时,先按顺序调用preHandle方法,然后逆序调用每个拦截器的postHandle和afterCompletion方法 -->  
</mvc:interceptors>

5.拦截器代码

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;
import com.xforce.charles.model.Admin;
import com.xforce.charles.model.Login;
import com.xforce.charles.util.JWT;
import com.xforce.charles.util.ResponseData;

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.getParameter("token");
    ResponseData responseData = ResponseData.ok();
    //token不存在
    if(null != token) {
        Login login = JWT.unsign(token, Login.class);
        String loginId = request.getParameter("loginId");
        //解密token后的loginId与用户传来的loginId不一致,一般都是token过期
        if(null != loginId && null != login) {
            if(Integer.parseInt(loginId) == login.getId()) {
                return true;
            } else {
                responseData = ResponseData.forbidden();
                responseMessage(response, response.getWriter(), responseData);
                return false;
            }
        } else {
            responseData = ResponseData.forbidden();
            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) {
    responseData = ResponseData.forbidden();
    response.setContentType("application/json; charset=utf-8");  
    String json = JSONObject.toJSONString(responseData);
    out.print(json);
    out.flush();
    out.close();
}

}

6.注意点:用@ResponseBody返回json数据时,有时会有乱码,需要在springmvc的配置文件里面加以下配置(spring4以上)

<mvc:annotation-driven>
   <mvc:message-converters register-defaults="true">
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="supportedMediaTypes" value = "text/plain;charset=UTF-8" />
    </bean>
   </mvc:message-converters>
</mvc:annotation-driven> 

7.最后分享一个类,用于返回给客户端的万能类,我觉得它可以满足一般的接口

import java.util.HashMap;
import java.util.Map;

public class ResponseData {
private final String message;
private final int code;
private final Map<String, Object> data = new HashMap<String, Object>();

public String getMessage() {
    return message;
}

public int getCode() {
    return code;
}

public Map<String, Object> getData() {
    return data;
}

public ResponseData putDataValue(String key, Object value) {
    data.put(key, value);
    return this;
}

private ResponseData(int code, String message) {
    this.code = code;
    this.message = message;
}

public static ResponseData ok() {
    return new ResponseData(200, "Ok");
}

public static ResponseData notFound() {
    return new ResponseData(404, "Not Found");
}

public static ResponseData badRequest() {
    return new ResponseData(400, "Bad Request");
}

public static ResponseData forbidden() {
    return new ResponseData(403, "Forbidden");
}

public static ResponseData unauthorized() {
    return new ResponseData(401, "unauthorized");
}

public static ResponseData serverInternalError() {
    return new ResponseData(500, "Server Internal Error");
}

public static ResponseData customerError() {
    return new ResponseData(1001, "customer Error");
}

}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值