• JWT公司的主流Json Web Token 令牌 如何使用,取代session


    JWT简介:

    JWT(JSON WEB TOKEN):JSON网络令牌,JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式在不同实体之间安全传输信息(JSON格式)。它是在Web环境下两个实体之间传输数据的一项标准。实际上传输的就是一个字符串。广义上讲JWT是一个标准的名称;狭义上JWT指的就是用来传递的那个token字符串

    JWT公司官网:   

    https://jwt.io/

    jwt的token结构:

    JWT的数据结构以及签发的过程
    JWT由三部分构成:header(头部)、payload(载荷)和signature(签名)。

    Header 头部信息:指定类型和算法
    Payload 荷载信息:存放Claims声明信息
    Signature 签名:把前两者对应的Json结构进行base64url编码之后的字符串拼接起来和密钥放一起加密后的签名
    组成方式为: header.payload.signature

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
    eyJpc3MiOiLlvKDlvLoiLCJuYW1lIjoiNzg5IiwiZXhwIjoxNTI4MzY0MjU5LCJpYXQiOjE1MjgzNjMwNTl9.574koY-c9SqMNNzfvAWQuKEnimWeZAcoFQ5XudNWF3o

    因为是拼接的所以base64很容易破解,所以你不要把密码或敏感的信息放入,这样就算是被别人破解了你的荷载信息他也只能看着,

    你该如何去理解JWT帮你做些什么?

      结合业务场景,首先前端登录 后端通过用户名与密码验证成功后,使用jwt生成 一个具备有效期的token令牌, 这个token里面存储着 token本身的结构,加上token的过期时间,和一个你自己定义的字符串类似于加密 签名所用的盐,这个盐只有你自己知道,这样你的加密才是安全的,还可以包含一些自定义的用户信息放在荷载信息里,

    本文需要你具备javaSE的基础, 所涉及的jar包如下:

    <!-- JWT javawebToken-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
            <!-- 阿里巴巴json -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.4</version>
            </dependency>

    jwt 如何生成Token呢?

    注意:这里使用的阿里巴巴提供的json工具包,实现序列化,后续会贴上所用的jar

    /**
     * token 工具类
     * @author 郎俊楠
     *
     */
    public class JwtUtil {
        private static Logger logger = Logger.getLogger(JwtUtil.class);
        public static final String TOKEN_HEADER = "Authorization";//token的key 也是名 不要写成token这样,要按照规范来
        public static final String TOKEN_PREFIX = "Bearer ";//token值的前缀,这是一种规范 ok
        private static final String SECRET = "mrLang";//你自己定的字符串 别让别人知道,加密时候用   盐
        public static final String FUNCTS = "FUNCTS";//获取用户的功能使用的key
        public static final String USERINFO = "USER";//获取用户使用的key
        private static final long EXPIRATION = 1800L;// token的生命周期30分
        /**
         * 创建token令牌 以下为参数都是自定义信息
         * @param loginName  一般我们放用户的唯一标识登录名
         * @param functs 当前用户的功能集合, 本人的rbac权限比较个性化且很负责,一般你们放role角色就可以了
         * @param user 当前用户
         * @return
         */
        public static String createToken(String loginName, List<Object> functs, Users user) {
            Map<String, Object> map = new HashMap<>();
            //当前用户拥有的功能
            map.put(FUNCTS, JsonUtil.set(functs));
            //当前用户信息
            map.put(USERINFO, JsonUtil.set(user));
            
            String token = Jwts.builder()
                    .setSubject(loginName)//主题  主角是谁?    赋值登录名
                    .setClaims(map)
                    .setIssuedAt(new Date())//设置发布时间,也是生成时间
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))//设置过期时间
                    .signWith(SignatureAlgorithm.HS256, SECRET)//设置HS256加密,并且把你的盐 放里,这里推荐使用SH256证书加密
                    .compact();//创建完成
            return token;
        }

    jwt 如何验证Token是否过期呢?

    // token是否过期
        public static boolean isExpiration(String token) {
            try {
                return getTokenBody(token).getExpiration().before(new Date());
            } catch (Exception e) {
                return true;
            }
        }

    jwt 如何获取自定义的信息呢?

    // 获取主角,登录名
        public static String getUserName(String token) {
            return getTokenBody(token).getSubject();
        }
    
        // 获取token中存储的功能
        public static List<Object> getUserFuncts(String token) {
            String str = getTokenBody(token).get(FUNCTS).toString();
            List<Object> list = JsonUtil.getArray(str);
            return list;
        }
    
        // 获取token存储的用户
        public static Object getUser(String token) {
            String str = getTokenBody(token).get(USERINFO).toString();
            return JsonUtil.getObj(str);
        }
    
        // 公共获取自定义数据
        public static Claims getTokenBody(String token) {
            return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        }

    jwt 如何刷新Token呢?

    // 刷新token
        public static String refreshToken(String token) {
            if (isExpiration(token)) {
                logger.info("token刷新失败!! 过期了!!");
                return null;
            }
            // 获取用户 权限信息
            String functs = getTokenBody(token).get(FUNCTS).toString();
            String user = getTokenBody(token).get(USERINFO).toString();
            String username = getTokenBody(token).getSubject();
            Map<String, Object> map = new HashMap<>();
            map.put(FUNCTS, JsonUtil.set(functs));
            map.put(USERINFO, JsonUtil.set(user));
            token = Jwts.builder().signWith(SignatureAlgorithm.HS256, SECRET).setClaims(map).setSubject(username)
                    .setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))
                    .compact();
            return token;
        }

    token 如何发送给前端?.

    传入 当前用户的 功能与用户信息,登录名 生成token,写入response的返回头中,前端获取后保存在前端的本地缓存中,后续前端请求要把token放在 头header里

    //登录成功之后
                    List<Object> functs=(List<Object>) authResult.getAuthorities();//当前功能列表
                    String loginName=authResult.getName();//登录名
                    Users obj=(Users)authResult.getPrincipal();//用户信息
                    String token=JwtUtil.createToken(loginName,functs,obj);//生成token
                    //TOKEN_HEADER= Authorization TOKEN_PREFIX=Bearer token值
                    response.setHeader(JwtUtil.TOKEN_HEADER,JwtUtil.TOKEN_PREFIX+token);
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_OK);
                    //个人编写的视图对象
                    DTO dto=new DTO<>();
                    dto.setCode("000000");
                    dto.setMessage("认证通过");
                    
                    PrintWriter pw=response.getWriter();
                    pw.write(JsonUtil.set(dto));//写入json
                    pw.flush();//强制刷新
                    pw.close();//关闭流

    用户请求携带token如何验证?

    你可以自定义一个过滤器,或者使用某某框架,自定义拦截器,或者aop

    String header = request.getHeader(JwtUtil.TOKEN_HEADER);
            if (null == header || !header.toLowerCase().startsWith(JwtUtil.TOKEN_PREFIX)) {
                // 如果头部 Authorization 未设置或者不是 basic 认证头部,则当前
                // 请求不是该过滤器关注的对象,直接放行,继续filter chain 的执行
                chain.doFilter(request, response);
                return;
            }
            try {
                String token = header.replace(JwtUtil.TOKEN_PREFIX, "");
                // 验证token是否过期
                if(JwtUtil.isExpiration(token)){
                    throw new javax.security.sasl.AuthenticationException("token 验证不通过");
                }
                //檢查token是否能解析
                Users user = (Users) JwtUtil.getUser(token);
                if (null == user) {
                    throw new javax.security.sasl.AuthenticationException("token 验证不通过");
                }
                
                //验证成功

    总结:

    使用jwt生成 一个具备有效期的token令牌, 这个token里面存储着 token本身的结构,加上token的过期时间,和一个你自己定义的字符串类似于加密 ,也就是签名所用的盐,这个盐只有你自己知道,这样你的加密才是安全的,还可以包含一些自定义的用户信息放在荷载信息里,本文所用的HS256+盐 对token加密是没有RS256安全的,

    JWT签名算法中HS256和RS256有什么区别?

    JWT签名算法中,一般有两个选择,一个采用HS256,另外一个就是采用RS256。
    签名实际上是一个加密的过程,生成一段标识(也是JWT的一部分)作为接收方验证信息是否被篡改的依据。

    RS256 (采用SHA-256 的 RSA 签名) 是一种非对称算法, 它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。由于公钥 (与私钥相比) 不需要保护, 因此大多数标识提供方使其易于使用方获取和使用 (通常通过一个元数据URL)。
    另一方面, HS256 (带有 SHA-256 的 HMAC 是一种对称算法, 双方之间仅共享一个 密钥。由于使用相同的密钥生成签名和验证签名, 因此必须注意确保密钥不被泄密。

    在开发应用的时候启用JWT,使用RS256更加安全,你可以控制谁能使用什么类型的密钥。另外,如果你无法控制客户端,无法做到密钥的完全保密,RS256会是个更佳的选择,JWT的使用方只需要知道公钥。

     建议:

    为了双重保险,建议您使用RS256,最好是生成一个证书 读取使用证书里面的公钥加密,私钥留着以后每次前端请求验证token的合法性就可以了
    jwt提供了方法可以验证token的合法性,过期时间,还可以根据token读取用户信息,这样你把token存储前端的localstorage中,
    后端的缓存都省了

    这样做安全么?
    你要知道 RS256非对称加密是一套体系的,公钥私钥是一组,都存在的后端,别人不进你服务器盗取你就是安全的,要是真能登录你服务器那就直接删你库了,

    本文作者为本人原创:如需转载 请携带出处 谢谢!!!!

  • 相关阅读:
    一个页面通过iframe,获取另一个页面的form
    framework7 点取消后还提交表单解决方案
    sys模块
    logging模块
    MongoDB
    os.path模块
    Redis 复制、Sentinel的搭建和原理说明
    Linux环境下虚拟环境virtualenv安装和使用
    centos7 下通过nginx+uwsgi部署django应用
    NGINX实现负载均衡的几种方式
  • 原文地址:https://www.cnblogs.com/langjunnan/p/6814710.html
Copyright © 2020-2023  润新知