zoukankan      html  css  js  c++  java
  • SpringBoot集成JWT验证方式

      为什么要告别session?有这样一个场景,系统的数据量达到千万级,需要几台服务器部署,当一个用户在其中一台服务器登录后,用session保存其登录信息,其他服务器怎么知道该用户登录了?(单点登录),当然解决办法有,可以用spring-session。如果该系统同时为移动端服务呢?移动端通过url向后台要数据,如果用session,通过sessionId识别用户,万一sessionId被截获了,别人可以利用sessionId向后台要数据,就有安全隐患了。所以有必要跟session说拜拜了。服务端不需要存储任何用户的信息,用户的验证应该放在客户端,jwt就是这种方式!

    什么是jwt?

    最详细的是官网:https://jwt.io/ 

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

    1.pom.xml 导入jwt的包

    <!-- jwt -->
    <!-- https://mvnrepository.com/artifact/com.auth0/java-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");
    }
    }
    

      

  • 相关阅读:
    SharePoint Framework (SPFx) 开发入门教程
    SharePoint 2013 Designer 入门教程
    SharePoint 2013 开发教程
    SharePoint 2013 入门教程
    SharePoint Online 部署SPFx Web部件
    SharePoint Online SPFx Web部件绑定数据
    SharePoint Online 创建SPFx客户端Web部件
    SharePoint Online 配置框架(SPFx)开发环境
    SharePoint Online 创建应用程序目录
    SharePoint Online 启用 IRM
  • 原文地址:https://www.cnblogs.com/liboware/p/12550584.html
Copyright © 2011-2022 走看看