• 基于JAVA JWT 实现OATUH TOKEN验证


     

    什么是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>

    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");
        }
    }
  • 相关阅读:
    CF(437C)The Child and Toy(馋)
    Android大放送干:书籍、过程、工具等各种全
    UVa 121
    ONOS系统架构演进,实现高可用性解决方案
    PowerDesigner 的常用小技巧 转
    现代团队必须是非常非常有活力、有激情同时又有向心力、有限属技能的团队。
    关于团队管理的心得体会 转
    Delphi 编译错误信息表
    财务基础 入门
    Sql Server CONVERT获取当前日期及日期样式
  • 原文地址:https://www.cnblogs.com/cfas/p/9384049.html
Copyright © 2020-2023  润新知