Spring boot JWT 实现(极简版)

水平有限,不喜勿喷
欢迎关注github,点击直达

  1. jwt 介绍

    • 主要分为三部分
      • Header(定义使用什么算法加解密)
      • Payload (需要放的内容)
      • Signature (类似于私钥,就好比与WX的私钥差不多,别人不知道的话拿到一串加密的数字无法解密)
  2. jwt与session的区别

    • 主要是session在集群之下共享的问题,jwt存在客户端 ,服务端只是做加密解密,所以性能很好,也解决了单点的问题
  3. 调用流程如下:
    在这里插入图片描述

  4. 首先在pom.xml文件加入依赖

            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
    
    		<!-- 处理时间 -->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>2.9.9</version>
            </dependency>
    
    		<!-- 模板引擎不是必要依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    
  5. application.yaml加入配置

    spring:
      freemarker:
        cache: false
        charset: UTF-8
        check-template-location: true
        suffix: .html
        template-loader-path: classpath:/templates
        request-context-attribute: request
    
  6. 增加cookie处理类,和jwt加解密类

    /**
     * @Project:
     * @Author: leegoo
     * @Date: 2019年07月22日
     */
    package cn.withmes.springboot.singlepointjwt;
    
    /**
     * ClassName: CookieUtils
     * @Description:
     * @author leegoo
     * @date 2019年07月22日
     */
    
    import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    /**
     *
     * Cookie 工具类
     *
     */
    public final class CookieUtils {
    
        /**
         * 得到Cookie的值, 不编码
         *
         * @param request
         * @param cookieName
         * @return
         */
        public static String getCookieValue(HttpServletRequest request, String cookieName) {
            return getCookieValue(request, cookieName, false);
        }
    
        /**
         * 得到Cookie的值,
         *
         * @param request
         * @param cookieName
         * @return
         */
        public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
            Cookie[] cookieList = request.getCookies();
            if (cookieList == null || cookieName == null) {
                return null;
            }
            String retValue = null;
            try {
                for (int i = 0; i < cookieList.length; i++) {
                    if (cookieList[i].getName().equals(cookieName)) {
                        if (isDecoder) {
                            retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
                        } else {
                            retValue = cookieList[i].getValue();
                        }
                        break;
                    }
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return retValue;
        }
    
        /**
         * 得到Cookie的值,
         *
         * @param request
         * @param cookieName
         * @return
         */
        public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
            Cookie[] cookieList = request.getCookies();
            if (cookieList == null || cookieName == null) {
                return null;
            }
            String retValue = null;
            try {
                for (int i = 0; i < cookieList.length; i++) {
                    if (cookieList[i].getName().equals(cookieName)) {
                        retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
                        break;
                    }
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return retValue;
        }
    
        /**
         * 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
         */
        public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                     String cookieValue) {
            setCookie(request, response, cookieName, cookieValue, -1);
        }
    
        /**
         * 设置Cookie的值 在指定时间内生效,但不编码
         */
        public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                     String cookieValue, int cookieMaxage) {
            setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
        }
    
        /**
         * 设置Cookie的值 不设置生效时间,但编码
         */
        public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                     String cookieValue, boolean isEncode) {
            setCookie(request, response, cookieName, cookieValue, -1, isEncode);
        }
    
        /**
         * 设置Cookie的值 在指定时间内生效, 编码参数
         */
        public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                     String cookieValue, int cookieMaxage, boolean isEncode) {
            doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
        }
    
        /**
         * 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
         */
        public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
                                     String cookieValue, int cookieMaxage, String encodeString) {
            doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
        }
    
        /**
         * 删除Cookie带cookie域名
         */
        public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
                                        String cookieName) {
            doSetCookie(request, response, cookieName, "", -1, false);
        }
    
        /**
         * 设置Cookie的值,并使其在指定时间内生效
         *
         * @param cookieMaxage cookie生效的最大秒数
         */
        private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                              String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
            try {
                if (cookieValue == null) {
                    cookieValue = "";
                } else if (isEncode) {
                    cookieValue = URLEncoder.encode(cookieValue, "utf-8");
                }
                Cookie cookie = new Cookie(cookieName, cookieValue);
                if (cookieMaxage > 0)
                    cookie.setMaxAge(cookieMaxage);
                if (null != request) {// 设置域名的cookie
                    String domainName = getDomainName(request);
                    System.out.println(domainName);
                    if (!"localhost".equals(domainName)) {
                        cookie.setDomain(domainName);
                    }
                }
                cookie.setPath("/");
                response.addCookie(cookie);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 设置Cookie的值,并使其在指定时间内生效
         *
         * @param cookieMaxage cookie生效的最大秒数
         */
        private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
                                              String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
            try {
                if (cookieValue == null) {
                    cookieValue = "";
                } else {
                    cookieValue = URLEncoder.encode(cookieValue, encodeString);
                }
                Cookie cookie = new Cookie(cookieName, cookieValue);
                if (cookieMaxage > 0)
                    cookie.setMaxAge(cookieMaxage);
                if (null != request) {// 设置域名的cookie
                    String domainName = getDomainName(request);
                    System.out.println(domainName);
                    if (!"localhost".equals(domainName)) {
                        cookie.setDomain(domainName);
                    }
                }
                cookie.setPath("/");
                response.addCookie(cookie);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 得到cookie的域名
         */
        private static final String getDomainName(HttpServletRequest request) {
            String domainName = null;
    
            String serverName = request.getRequestURL().toString();
            if (serverName == null || serverName.equals("")) {
                domainName = "";
            } else {
                serverName = serverName.toLowerCase();
                serverName = serverName.substring(7);
                final int end = serverName.indexOf("/");
                serverName = serverName.substring(0, end);
                final String[] domains = serverName.split("\\.");
                int len = domains.length;
                if (len > 3) {
                    // www.xxx.com.cn
                    domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
                } else if (len <= 3 && len > 1) {
                    // xxx.com or xxx.cn
                    domainName = "." + domains[len - 2] + "." + domains[len - 1];
                } else {
                    domainName = serverName;
                }
            }
    
            if (domainName != null && domainName.indexOf(":") > 0) {
                String[] ary = domainName.split("\\:");
                domainName = ary[0];
            }
            return domainName;
        }
    
    }
    
    /**
     * @Project:
     * @Author: leegoo
     * @Date: 2019年07月22日
     */
    package cn.withmes.springboot.singlepointjwt;
    
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jws;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import javax.crypto.spec.SecretKeySpec;
    import javax.xml.bind.DatatypeConverter;
    import java.util.Map;
    
    /**
     * ClassName: JWTUtil
     *
     * @author leegoo
     * @Description: jwt 工具类
     * @date 2019年07月22日
     */
    public class JWTUtil {
    
        public static String generatorToken(Map<String, Object> payload) {
    
            try {
                String token = Jwts.builder().setPayload(new ObjectMapper().writeValueAsString(payload))
                        .signWith(SignatureAlgorithm.HS256, createKey())
                        .compact();
                return token;
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
        private static SecretKeySpec createKey() {
            byte[] bin = DatatypeConverter.parseBase64Binary("leegookey");
            return new SecretKeySpec(bin, SignatureAlgorithm.HS256.getJcaName());
        }
    
        public static Claims parseToken(String key) {
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(createKey()).parseClaimsJws(key);
            return claimsJws.getBody();
        }
    }
    
    
  7. 编写注解类,只有带了注解的才去拦截

    /**
     * @Project:
     * @Author: leegoo
     * @Date: 2019年07月22日
     */
    package cn.withmes.springboot.singlepointjwt;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * ClassName: JWTAnnotation
     * @Description: 标记需要拦截的类
     * @author leegoo
     * @date 2019年07月22日
     */
    
    @Documented
    @Target({ElementType.METHOD})
    @Inherited
    @Retention(value = RetentionPolicy.RUNTIME)
    public @interface JWTAnnotation  {
    }
    
    
  8. 编写返回体

    /**
     * @Project:
     * @Author: leegoo
     * @Date: 2019年07月22日
     */
    package cn.withmes.springboot.singlepointjwt;
    
    /**
     * ClassName: Response
     * @Description: 响应体
     * @author leegoo
     * @date 2019年07月22日
     */
    public class ResponseData<E> {
    
        private  String token ;
    
        private E data;
    
        private  int code ;
    
        private String msg ;
    
        public ResponseData( ) {
            this.code = -1;
        }
    
        public ResponseData(int code) {
            this.code = code;
        }
    
        public String getToken() {
            return token;
        }
    
        public void setToken(String token) {
            this.token = token;
        }
    
        public E getData() {
            return data;
        }
    
        public void setData(E data) {
            this.data = data;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    }
    
    
  9. 编写控制层

    /**
     * @Project:
     * @Author: leegoo
     * @Date: 2019年07月22日
     */
    package cn.withmes.springboot.singlepointjwt;
    
    import org.joda.time.DateTime;
    import org.springframework.lang.NonNull;
    import org.springframework.lang.Nullable;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.annotation.PostConstruct;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * ClassName: MyController
     *
     * @author leegoo
     * @Description:
     * @date 2019年07月22日
     */
    @Controller
    public class MyController {
    
        /**
         * @Description:模拟保存用户数据
         * @param: 
         * @return: 
         * @auther: liming
         * @date: 7/23/2019 7:26 PM
         */
        public static Map<String, User> userinfoMap = new ConcurrentHashMap<>(16);
    
        @PostConstruct
        public void init() {
            userinfoMap.put("zhangfei", new User("zhangfei", "123"));
        }
    
    
        @RequestMapping(value = "index", method = RequestMethod.GET)
        public ModelAndView index() {
            return new ModelAndView("index");
        }
    
    
        @GetMapping("/error")
        public ModelAndView error(HttpServletRequest request) {
            String msg = request.getParameter("msg");
            ModelAndView view = new ModelAndView();
            view.addObject("msg", msg);
            return view;
        }
    
        
        /**
         * @Description:展示信息
         * @param: 
         * @return: 
         * @auther: liming
         * @date: 7/23/2019 7:25 PM
         */
        @RequestMapping(value = "/login/showinfo", method = RequestMethod.GET)
        @ResponseBody
        @JWTAnnotation
        public ModelAndView showinfo(String username) {
            if (StringUtils.isEmpty(username)) {
                ModelAndView info = new ModelAndView("error");
                info.addObject("errorMsg", "账号为空!");
                return info;
            }
            ModelAndView info = new ModelAndView("info");
            User user = userinfoMap.get(username);
            user.setPwd(null);
            info.addObject("Info", user);
            return info;
        }
    
    
        /**
         * @Description:登陆
         * @param: 
         * @return: 
         * @auther: liming
         * @date: 7/23/2019 7:25 PM
         */
        @GetMapping("/login/in")
        @ResponseBody
        public ResponseData<User> loginin(@NonNull String username, @NonNull String pwd, HttpServletResponse response) {
            ResponseData<User> data = null;
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(pwd)) {
                data = new ResponseData<>();
                data.setMsg("用户名或者密码不能为空");
                return data;
            }
            if (!userinfoMap.containsKey(username) || !userinfoMap.get(username).getPwd().equalsIgnoreCase(pwd))
                return new ResponseData<>(-1);
            data = createUserToken(username);
            response.addHeader("Set-Cookie", "aceess_token=" + data.getToken());
            return data;
        }
    
        
        /**
         * @Description:生成token 
         * @param: 
         * @return: 
         * @auther: liming
         * @date: 7/23/2019 7:26 PM
         */
        public static ResponseData<User> createUserToken(@NonNull String username) {
            ResponseData<User> responseData = new ResponseData<>(-1);
            User user = userinfoMap.get(username);
            Map<String, Object> payload = new HashMap<>();
            payload.put("uukey", user.getUsername());
            payload.put("exp", DateTime.now().plusSeconds(40).toDate().getTime() / 1000);
            String token = JWTUtil.generatorToken(payload);
            responseData.setCode(0);
            responseData.setToken(token);
            responseData.setData(user);
            return responseData;
        }
    
    
    }
    
    
  10. 前面这些基本工作做完了,然后编写拦截器, 主要针对带有JWTAnnotation 的方法进行拦截

    /**
     * @Project:
     * @Author: leegoo
     * @Date: 2019年07月22日
     */
    package cn.withmes.springboot.singlepointjwt;
    
    import com.sun.javafx.geom.transform.SingularMatrixException;
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.ExpiredJwtException;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.util.StringUtils;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * ClassName: JWTInteruptHandler
     *
     * @author leegoo
     * @Description:
     * @date 2019年07月22日
     */
    @Configuration
    public class JWTInteruptHandler extends HandlerInterceptorAdapter implements WebMvcConfigurer {
    
        private final String ACCESS_TOKEN = "aceess_token";
    
        //拦截路径  需要实现 WebMvcConfigurer
        // 可添加多个
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new JWTInteruptHandler()).addPathPatterns("/login/showinfo");
            registry.addInterceptor(new JWTInteruptHandler()).excludePathPatterns("/resource");
            registry.addInterceptor(new JWTInteruptHandler()).excludePathPatterns("/login/in");
            registry.addInterceptor(new JWTInteruptHandler()).excludePathPatterns("/index");
            registry.addInterceptor(new JWTInteruptHandler()).excludePathPatterns("/error");
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HandlerMethod handlerMethod = null;
            if (handler instanceof org.springframework.web.method.HandlerMethod) {
                handlerMethod = (HandlerMethod) handler;
            } else {
                return true;
            }
            if (checkAnnotation(handlerMethod)) return true;
    
            String cookieValue = CookieUtils.getCookieValue(request, this.ACCESS_TOKEN);
    
            // 如果cookie 没有 token 那么跳转到首页
            if (StringUtils.isEmpty(cookieValue)) {
                response.sendRedirect("/index");
                return false;
            }
    
            //解析jwt
            try {
                Claims claims = JWTUtil.parseToken(cookieValue);
                System.out.println(claims);
            } catch (ExpiredJwtException e) {
                dispose(response, "expire token .");
                return false;
            } catch (SingularMatrixException e) {
                dispose(response, "sign exception .");
                return false;
            }
            return super.preHandle(request, response, handler);
        }
    
        private void dispose(HttpServletResponse response, String msg) throws IOException {
            response.sendRedirect("/error?msg=" + msg);
        }
    
        /**
         * @Description:检查是否带有注解
         * @param:
         * @return:
         * @auther: liming
         * @date: 7/23/2019 7:27 PM
         */
        private boolean checkAnnotation(HandlerMethod handlerMethod) {
            return null == handlerMethod.getMethod().getAnnotation(JWTAnnotation.class);
        }
    }
    
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值