jwt_jwt token - CSDN
jwt 订阅
智威汤逊(JWT)一直以“不断自我创新,也不断创造广告事业”著称于世。JWT首开先例的顾客产品调查、第一本杂志指南、第一本农业指南、提供给国际投资人的第一本行销指南、制作第一个电台表演秀、制作第一个商业电视传播、第一个使用电脑策划及媒体购买……。智威汤逊以品牌全行销规划(Thompson Total Branding),结合广告、直效行销、促销、赞助及公关活动,致力于协助客户达成短期业绩成长,并创造长期的品牌价值。 展开全文
智威汤逊(JWT)一直以“不断自我创新,也不断创造广告事业”著称于世。JWT首开先例的顾客产品调查、第一本杂志指南、第一本农业指南、提供给国际投资人的第一本行销指南、制作第一个电台表演秀、制作第一个商业电视传播、第一个使用电脑策划及媒体购买……。智威汤逊以品牌全行销规划(Thompson Total Branding),结合广告、直效行销、促销、赞助及公关活动,致力于协助客户达成短期业绩成长,并创造长期的品牌价值。
信息
JWT
是智威汤逊的英文缩写
全球第一家
广告公司
中文名
JWT
创始于
1864年
JWT简介
自成立以来,时至今日,140周岁的JWT风采依旧,昂首跻身于世界4大顶尖广告公司之列。JWT的大家庭有10,000多名成员,300多个分公司、办事处遍布全球六大洲主要城市,为客户提供全方位的品牌服务。目前智威汤逊隶属于全球最大的传播集团WPP。
收起全文
精华内容
参与话题
  • 本课程全程使用目前比较流行的开发工具idea进行开发,采用现在互联网流行的微服务架构SpringBoot+SpringCloud+JPA, 同时也使用了互联网的高并发中间件redis,ElasticSearch,RabbitMQ,MongoDB数据库,springSecurity...
  • JWT产生和验证Token

    万次阅读 多人点赞 2019-07-31 19:15:33
    Token验证  最近了解下基于 Token 的身份验证,跟大伙分享下。很多大型网站也都在用,比如 Facebook,Twitter,Google+,Github 等等,比起传统的身份验证方法,Token 扩展性更强,也更安全点,非常适合用在 Web...

    #Token验证
      最近了解下基于 Token 的身份验证,跟大伙分享下。很多大型网站也都在用,比如 Facebook,Twitter,Google+,Github 等等,比起传统的身份验证方法,Token 扩展性更强,也更安全点,非常适合用在 Web 应用或者移动应用上。Token 的中文有人翻译成 “令牌”,我觉得挺好,意思就是,你拿着这个令牌,才能过一些关卡。
    ##传统的Token验证
      HTTP 是一种没有状态的协议,也就是它并不知道是谁是访问应用。这里我们把用户看成是客户端,客户端使用用户名还有密码通过了身份验证,不过下回这个客户端再发送请求时候,还得再验证一下。
      解决的方法就是,当用户请求登录的时候,如果没有问题,我们在服务端生成一条记录,这个记录里可以说明一下登录的用户是谁,然后把这条记录的 ID 号发送给客户端,客户端收到以后把这个 ID 号存储在 Cookie 里,下次这个用户再向服务端发送请求的时候,可以带着这个 Cookie ,这样服务端会验证一个这个 Cookie 里的信息,看看能不能在服务端这里找到对应的记录,如果可以,说明用户已经通过了身份验证,就把用户请求的数据返回给客户端。
      上面说的就是 Session,我们需要在服务端存储为登录的用户生成的 Session ,这些 Session 可能会存储在内存,磁盘,或者数据库里。我们可能需要在服务端定期的去清理过期的 Session 。
      ##基于 Token 的身份验证方法
      使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
    客户端使用用户名跟密码请求登录
    服务端收到请求,去验证用户名与密码
    验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
    客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
    客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
    服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据
    ##JWT+HA256验证
    实施 Token 验证的方法挺多的,还有一些标准方法,比如 JWT,读作:jot ,表示:JSON Web Tokens 。JWT 标准的 Token 有三个部分:
    header
    payload
    signature
    中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
    

    ###Header
    header 部分主要是两部分内容,一个是 Token 的类型,另一个是使用的算法,比如下面类型就是 JWT,使用的算法是 HS256,就是SHA-256,和md5一样是不可逆的散列算法。

    {
      "typ": "JWT",
      "alg": "HS256"
    }
    

    上面的内容要用 Base64 的形式编码一下,所以就变成这样:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    

    ###Payload
    Payload 里面是 Token 的具体内容,这些内容里面有一些是标准字段,你也可以添加其它需要的内容。下面是标准字段:
    iss:Issuer,发行者
    sub:Subject,主题
    aud:Audience,观众
    exp:Expiration time,过期时间
    nbf:Not before
    iat:Issued at,发行时间
    jti:JWT ID
    比如下面这个 Payload ,用到了 iss 发行人,还有 exp 过期时间。另外还有两个自定义的字段,一个是 name ,还有一个是 admin 。

    { 
     "iss": "ninghao.net",
     "exp": "1438955445",
     "name": "wanghao",
     "admin": true
    }
    

    使用 Base64 编码以后就变成了这个样子:

    eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ
    

    ###Signature
    JWT 的最后一部分是 Signature ,这部分内容有三个部分,先是用 Base64 编码的 header.payload ,再用加密算法加密一下,加密的时候要放进去一个 Secret ,这个相当于是一个密码,这个密码秘密地存储在服务端。

    var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); 
    HMACSHA256(encodedString, 'secret');
    

    处理完成以后看起来像这样:

    SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
    

    最后这个在服务端生成并且要发送给客户端的 Token 看起来像这样:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
    

    客户端收到这个 Token 以后把它存储下来,下会向服务端发送请求的时候就带着这个 Token 。服务端收到这个 Token ,然后进行验证,通过以后就会返回给客户端想要的资源。验证的过程就是
    根据传过来的token再生成一下第三部分Signature,然后两个比对一下,一致就验证通过。

    展开全文
  • 使用JWT实现单点登录(完全跨域方案)

    万次阅读 多人点赞 2018-09-10 15:57:56
    首先介绍一下什么是JSON Web Token(JWT)? 官方文档是这样解释的:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过...

    首先介绍一下什么是JSON Web Token(JWT)?

    官方文档是这样解释的:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
    虽然JWT可以加密以在各方之间提供保密,但只将专注于签名令牌。签名令牌可以验证其中包含的声明的完整性,而加密令牌则隐藏其他方的声明。当使用公钥/私钥对签署令牌时,签名还证明只有持有私钥的一方是签署私钥的一方。

    通俗来讲,JWT是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,请求头中携带JWT串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据。

    什么情况下使用JWT比较适合?

    • 授权:这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛;
    • 信息交换:JWT是在各个服务之间安全传输信息的好方法。因为JWT可以签名,例如,使用公钥/私钥对儿 - 可以确定请求方是合法的。此外,由于使用标头和有效负载计算签名,还可以验证内容是否未被篡改。

    JWT的结构体是什么样的?

    JWT由三部分组成,分别是头信息、有效载荷、签名,中间以(.)分隔,如下格式:

    xxx.yyy.zzz
    

    header(头信息)
    由两部分组成,令牌类型(即:JWT)、散列算法(HMAC、RSASSA、RSASSA-PSS等),例如:

    {
      "alg": "HS256",
      "typ": "JWT"
    }
    

    然后,这个JSON被编码为Base64Url,形成JWT的第一部分。

    Payload(有效载荷)
    JWT的第二部分是payload,其中包含claims。claims是关于实体(常用的是用户信息)和其他数据的声明,claims有三种类型: registered, public, and private claims。
    Registered claims: 这些是一组预定义的claims,非强制性的,但是推荐使用, iss(发行人), exp(到期时间), sub(主题), aud(观众)等;
    Public claims: 自定义claims,注意不要和JWT注册表中属性冲突,这里可以查看JWT注册表
    Private claims: 这些是自定义的claims,用于在同意使用这些claims的各方之间共享信息,它们既不是Registered claims,也不是Public claims。
    以下是payload示例:

    {
      "sub": "1234567890",
      "name": "John Doe",
      "admin": true
    }
    

    然后,再经过Base64Url编码,形成JWT的第二部分;

    注意:对于签名令牌,此信息虽然可以防止篡改,但任何人都可以读取。除非加密,否则不要将敏感信息放入到Payload或Header元素中。

    Signature
    要创建签名部分,必须采用编码的Header,编码的Payload,秘钥,Header中指定的算法,并对其进行签名。
    例如,如果要使用HMAC SHA256算法,将按以下方式创建签名:

    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)
    

    签名用于验证消息在此过程中未被篡改,并且,在使用私钥签名令牌的情况下,它还可以验证JWT的请求方是否是它所声明的请求方。
    输出是三个由点分隔的Base64-URL字符串,可以在HTML和HTTP环境中轻松传递,与SAML等基于XML的标准相比更加紧凑。
    例如:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
    SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    

    JWT工作机制?

    在身份验证中,当用户使用其凭据成功登录时,将返回JSON Web Token(即:JWT)。由于令牌是凭证,因此必须非常小心以防止出现安全问题。一般情况下,不应将令牌保留的时间超过要求。理论上超时时间越短越好。

    每当用户想要访问受保护的路由或资源时,用户代理应该使用Bearer模式发送JWT,通常在Authorization header中。标题内容应如下所示:

    Authorization: Bearer <token>
    

    在某些情况下,这可以作为无状态授权机制。服务器的受保护路由将检查Authorization header中的有效JWT ,如果有效,则允许用户访问受保护资源。如果JWT包含必要的数据,则可以减少查询数据库或缓存信息。
    如果在Authorization header中发送令牌,则跨域资源共享(CORS)将不会成为问题,因为它不使用cookie。

    注意:使用签名令牌,虽然他们无法更改,但是令牌中包含的所有信息都会向用户或其他方公开。这意味着不应该在令牌中放置敏感信息。

    使用JWT的好处是什么?

    相比Simple Web Tokens (SWT)(简单Web令牌) and Security Assertion Markup Language Tokens (SAML)(安全断言标记语言令牌)

    • JWT比SAML更简洁,在HTML和HTTP环境中传递更方便;
    • 在安全方面,SWT只能使用HMAC算法通过共享密钥对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公钥/私钥对进行签名。与签名JSON的简单性相比,使用XML数字签名可能会存在安全漏洞;
    • JSON解析成对象相比XML更流行、方便。

    以下是我实际项目中的应用分析

    首先看一下大致的架构及流程图:
    架构图
    流程图

    主要有以下三步:

    • 项目一开始我先封装了一个JWTHelper工具包(GitHub下载),主要提供了生成JWT、解析JWT以及校验JWT的方法,其他还有一些加密相关操作,稍后我会以代码的形式介绍下代码。工具包写好后我将打包上传到私服,能够随时依赖下载使用;
    • 接下来,我在客户端项目中依赖JWTHelper工具包,并添加Interceptor拦截器,拦截需要校验登录的接口。拦截器中校验JWT有效性,并在response中重新设置JWT的新值;
    • 最后在JWT服务端,依赖JWT工具包,在登录方法中,需要在登录校验成功后调用生成JWT方法,生成一个JWT令牌并且设置到response的header中。

    以下是部分代码分享:

    • JwtHelper工具类:
    /**
     * @Author: Helon
     * @Description: JWT工具类
     * 参考官网:https://jwt.io/
     * JWT的数据结构为:A.B.C三部分数据,由字符点"."分割成三部分数据
     * A-header头信息
     * B-payload 有效负荷 一般包括:已注册信息(registered claims),公开数据(public claims),私有数据(private claims)
     * C-signature 签名信息 是将header和payload进行加密生成的
     * @Data: Created in 2018/7/19 14:11
     * @Modified By:
     */
    public class JwtHelper {
    
        private static Logger logger = LoggerFactory.getLogger(JwtHelper.class);
    
        /**
         * @Author: Helon
         * @Description: 生成JWT字符串
         * 格式:A.B.C
         * A-header头信息
         * B-payload 有效负荷
         * C-signature 签名信息 是将header和payload进行加密生成的
         * @param userId - 用户编号
         * @param userName - 用户名
         * @param identities - 客户端信息(变长参数),目前包含浏览器信息,用于客户端拦截器校验,防止跨域非法访问
         * @Data: 2018/7/28 19:26
         * @Modified By:
         */
        public static String generateJWT(String userId, String userName, String ...identities) {
            //签名算法,选择SHA-256
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
            //获取当前系统时间
            long nowTimeMillis = System.currentTimeMillis();
            Date now = new Date(nowTimeMillis);
            //将BASE64SECRET常量字符串使用base64解码成字节数组
            byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SecretConstant.BASE64SECRET);
            //使用HmacSHA256签名算法生成一个HS256的签名秘钥Key
            Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
            //添加构成JWT的参数
            Map<String, Object> headMap = new HashMap<>();
            /*
                Header
                {
                  "alg": "HS256",
                  "typ": "JWT"
                }
             */
            headMap.put("alg", SignatureAlgorithm.HS256.getValue());
            headMap.put("typ", "JWT");
            JwtBuilder builder = Jwts.builder().setHeader(headMap)
                    /*
                        Payload
                        {
                          "userId": "1234567890",
                          "userName": "John Doe",
                        }
                     */
                    //加密后的客户编号
                    .claim("userId", AESSecretUtil.encryptToStr(userId, SecretConstant.DATAKEY))
                    //客户名称
                    .claim("userName", userName)
                    //客户端浏览器信息
                    .claim("userAgent", identities[0])
                    //Signature
                    .signWith(signatureAlgorithm, signingKey);
            //添加Token过期时间
            if (SecretConstant.EXPIRESSECOND >= 0) {
                long expMillis = nowTimeMillis + SecretConstant.EXPIRESSECOND;
                Date expDate = new Date(expMillis);
                builder.setExpiration(expDate).setNotBefore(now);
            }
            return builder.compact();
        }
    
        /**
         * @Author: Helon
         * @Description: 解析JWT
         * 返回Claims对象
         * @param jsonWebToken - JWT
         * @Data: 2018/7/28 19:25
         * @Modified By:
         */
        public static Claims parseJWT(String jsonWebToken) {
            Claims claims = null;
            try {
                if (StringUtils.isNotBlank(jsonWebToken)) {
                    //解析jwt
                    claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SecretConstant.BASE64SECRET))
                            .parseClaimsJws(jsonWebToken).getBody();
                }else {
                    logger.warn("[JWTHelper]-json web token 为空");
                }
            } catch (Exception e) {
                logger.error("[JWTHelper]-JWT解析异常:可能因为token已经超时或非法token");
            }
            return claims;
        }
    
        /**
         * @Author: Helon
         * @Description: 校验JWT是否有效
         * 返回json字符串的demo:
         * {"freshToken":"A.B.C","userName":"Judy","userId":"123", "userAgent":"xxxx"}
         * freshToken-刷新后的jwt
         * userName-客户名称
         * userId-客户编号
         * userAgent-客户端浏览器信息
         * @param jsonWebToken - JWT
         * @Data: 2018/7/24 15:28
         * @Modified By:
         */
        public static String validateLogin(String jsonWebToken) {
            Map<String, Object> retMap = null;
            Claims claims = parseJWT(jsonWebToken);
            if (claims != null) {
                //解密客户编号
                String decryptUserId = AESSecretUtil.decryptToStr((String)claims.get("userId"), SecretConstant.DATAKEY);
                retMap = new HashMap<>();
                //加密后的客户编号
                retMap.put("userId", decryptUserId);
                //客户名称
                retMap.put("userName", claims.get("userName"));
                //客户端浏览器信息
                retMap.put("userAgent", claims.get("userAgent"));
                //刷新JWT
                retMap.put("freshToken", generateJWT(decryptUserId, (String)claims.get("userName"), (String)claims.get("userAgent"), (String)claims.get("domainName")));
            }else {
                logger.warn("[JWTHelper]-JWT解析出claims为空");
            }
            return retMap!=null?JSONObject.toJSONString(retMap):null;
        }
    
        public static void main(String[] args) {
           String jsonWebKey = generateJWT("123", "Judy",
                   "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36");
           System.out.println(jsonWebKey);
           Claims claims =  parseJWT(jsonWebKey);
            System.out.println(claims);
           System.out.println(validateLogin(jsonWebKey));
        }
    
    • AES加密工具类:
    /**
     * @Author: Helon
     * @Description: AES加密工具类
     * @Data: Created in 2018/7/28 18:38
     * @Modified By:
     */
    public class AESSecretUtil {
    
        /**秘钥的大小*/
        private static final int KEYSIZE = 128;
        
        /**
         * @Author: Helon
         * @Description: AES加密
         * @param data - 待加密内容
         * @param key - 加密秘钥
         * @Data: 2018/7/28 18:42
         * @Modified By:
         */
        public static byte[] encrypt(String data, String key) {
            if(StringUtils.isNotBlank(data)){
                try {
                    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
                    //选择一种固定算法,为了避免不同java实现的不同算法,生成不同的密钥,而导致解密失败
                    SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
                    random.setSeed(key.getBytes());
                    keyGenerator.init(KEYSIZE, random);
                    SecretKey secretKey = keyGenerator.generateKey();
                    byte[] enCodeFormat = secretKey.getEncoded();
                    SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
                    Cipher cipher = Cipher.getInstance("AES");// 创建密码器
                    byte[] byteContent = data.getBytes("utf-8");
                    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// 初始化
                    byte[] result = cipher.doFinal(byteContent);
                    return result; // 加密
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
        /**
         * @Author: Helon
         * @Description: AES加密,返回String
         * @param data - 待加密内容
         * @param key - 加密秘钥
         * @Data: 2018/7/28 18:59
         * @Modified By:
         */
        public static String encryptToStr(String data, String key){
    
            return StringUtils.isNotBlank(data)?parseByte2HexStr(encrypt(data, key)):null;
        }
    
    
        /**
         * @Author: Helon
         * @Description: AES解密
         * @param data - 待解密字节数组
         * @param key - 秘钥
         * @Data: 2018/7/28 19:01
         * @Modified By:
         */
        public static byte[] decrypt(byte[] data, String key) {
            if (ArrayUtils.isNotEmpty(data)) {
                try {
                    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
                    //选择一种固定算法,为了避免不同java实现的不同算法,生成不同的密钥,而导致解密失败
                    SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
                    random.setSeed(key.getBytes());
                    keyGenerator.init(KEYSIZE, random);
                    SecretKey secretKey = keyGenerator.generateKey();
                    byte[] enCodeFormat = secretKey.getEncoded();
                    SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");
                    Cipher cipher = Cipher.getInstance("AES");// 创建密码器
                    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 初始化
                    byte[] result = cipher.doFinal(data);
                    return result; // 加密
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    
        /**
         * @Author: Helon
         * @Description: AES解密,返回String
         * @param enCryptdata - 待解密字节数组
         * @param key - 秘钥
         * @Data: 2018/7/28 19:01
         * @Modified By:
         */
        public static String decryptToStr(String enCryptdata, String key) {
            return StringUtils.isNotBlank(enCryptdata)?new String(decrypt(parseHexStr2Byte(enCryptdata), key)):null;
        }
    
        /**
         * @Author: Helon
         * @Description: 将二进制转换成16进制
         * @param buf - 二进制数组
         * @Data: 2018/7/28 19:12
         * @Modified By:
         */
        public static String parseByte2HexStr(byte buf[]) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < buf.length; i++) {
                String hex = Integer.toHexString(buf[i] & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                sb.append(hex.toUpperCase());
            }
            return sb.toString();
        }
    
        /**
         * @Author: Helon
         * @Description: 将16进制转换为二进制
         * @param hexStr - 16进制字符串
         * @Data: 2018/7/28 19:13
         * @Modified By:
         */
        public static byte[] parseHexStr2Byte(String hexStr) {
            if (hexStr.length() < 1)
                return null;
            byte[] result = new byte[hexStr.length()/2];
            for (int i = 0;i< hexStr.length()/2; i++) {
                int high = Integer.parseInt(hexStr.substring(i*2, i*2+1), 16);
                int low = Integer.parseInt(hexStr.substring(i*2+1, i*2+2), 16);
                result[i] = (byte) (high * 16 + low);
            }
            return result;
        }
    
        public static void main(String[] args) {
            String ss = encryptToStr("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiIxMjMiLCJ1c2VyTmFtZSI6Ikp1ZHkiLCJleHAiOjE1MzI3Nzk2MjIsIm5iZiI6MTUzMjc3NzgyMn0.sIw_leDZwG0pJ8ty85Iecd_VXjObYutILNEwPUyeVSo", SecretConstant.DATAKEY);
            System.out.println(ss);
            System.out.println(decryptToStr(ss, SecretConstant.DATAKEY));
        }
    
    • 所使用的常量类:
    /**
     * @Author: Helon
     * @Description: JWT使用常量值
     * @Data: Created in 2018/7/27 14:37
     * @Modified By:
     */
    public class SecretConstant {
    
        //签名秘钥 自定义
        public static final String BASE64SECRET = "***********";
    
        //超时毫秒数(默认30分钟)
        public static final int EXPIRESSECOND = 1800000;
    
        //用于JWT加密的密匙 自定义
        public static final String DATAKEY = "************";
    
    }
    
    • 客户端pom依赖:
            <!--jwt工具类-->
            <dependency>
                <groupId>com.chtwm.component</groupId>
                <artifactId>jwt-helper</artifactId>
                <version>xxx</version>
            </dependency>
    
    • 客户端拦截器:
    /**
     * @Author: Helon
     * @Description: 校验是否登录拦截器
     * @Data: Created in 2018/7/30 14:30
     * @Modified By:
     */
    @Slf4j
    public class ValidateLoginInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
            //首先从请求头中获取jwt串,与页面约定好存放jwt值的请求头属性名为User-Token
            String jwt = httpServletRequest.getHeader("User-Token");
            log.info("[登录校验拦截器]-从header中获取的jwt为:{}", jwt);
            //判断jwt是否有效
            if(StringUtils.isNotBlank(jwt)){
                //校验jwt是否有效,有效则返回json信息,无效则返回空
                String retJson = JwtHelper.validateLogin(jwt);
                log.info("[登录校验拦截器]-校验JWT有效性返回结果:{}", retJson);
                //retJSON为空则说明jwt超时或非法
                if(StringUtils.isNotBlank(retJson)){
                    JSONObject jsonObject = JSONObject.parseObject(retJson);
                    //校验客户端信息
                    String userAgent = httpServletRequest.getHeader("User-Agent");
                    if (userAgent.equals(jsonObject.getString("userAgent"))) {
                        //获取刷新后的jwt值,设置到响应头中
                        httpServletResponse.setHeader("User-Token", jsonObject.getString("freshToken"));
                        //将客户编号设置到session中
                        httpServletRequest.getSession().setAttribute(GlobalConstant.SESSION_CUSTOMER_NO_KEY, jsonObject.getString("userId"));
                        return true;
                    }else{
                        log.warn("[登录校验拦截器]-客户端浏览器信息与JWT中存的浏览器信息不一致,重新登录。当前浏览器信息:{}", userAgent);
                    }
                }else {
                    log.warn("[登录校验拦截器]-JWT非法或已超时,重新登录");
                }
            }
            //输出响应流
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("hmac", "");
            jsonObject.put("status", "");
            jsonObject.put("code", "4007");
            jsonObject.put("msg", "未登录");
            jsonObject.put("data", "");
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");
            httpServletResponse.getOutputStream().write(jsonObject.toJSONString().getBytes("UTF-8"));
            return false;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    
        }
    }
    
    • 客户端拦截器在XML文件中配置:
    	<!--拦截器配置-->
        <mvc:interceptors>
            <mvc:interceptor>
    	        <!--需拦截url配置-->
                <mvc:exclude-mapping path="/api/aa/bb/**" />
                <mvc:exclude-mapping path="/api/aa/cc/test" />
                <bean id="validateLoginInterceptor" class="com.xxx.ValidateLoginInterceptor" />
            </mvc:interceptor>
        </mvc:interceptors>
    

    到此,后台服务的配置已经完成,下一步就需要前端页面将JWT令牌从response响应头中取出,然后存入localstorage或cookie中。但是遇到跨域场景,处理起来就会比较复杂,因为一旦在浏览器中跨域将获取不到localstorage中的JWT令牌。例如www.a.com域下的JWT,在www.b.com域下是获取不到的,所以我选择了一种页面跨域的方式进行处理,使用iframe+H5的postMessage(参考博文),具体我使用代码分享的方式来分析。

    • 前端页面js代码(服务端):
        /**CURD本地存储信息 start**/
              (function(doc,win){
                  var fn=function(){};
                  fn.prototype={
                      /*本地数据存储 t:cookie有效时间,单位s; domain:cookie存储所属的domain域*/
                      setLocalCookie: function (k, v, t,domain) {
                          //如果当前浏览器不支持localStorage将存储在cookie中
                          typeof window.localStorage !== "undefined" ? localStorage.setItem(k, v) :
                              (function () {
                                  t = t || 365 * 12 * 60 * 60;
                                  domain=domain?domain:".jwtserver.com";
                                  document.cookie = k + "=" + v + ";max-age=" + t+";domain="+domain+";path=/";
                              })()
                      },
                      /*获取本地存储数据*/
                      getLocalCookie: function (k) {
                          k = k || "localDataTemp";
                          return typeof window.localStorage !== "undefined" ? localStorage.getItem(k) :
                              (function () {
                                  var all = document.cookie.split(";");
                                  var cookieData = {};
                                  for (var i = 0, l = all.length; i < l; i++) {
                                      var p = all[i].indexOf("=");
                                      var dataName = all[i].substring(0, p).replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");
                                      cookieData[dataName] = all[i].substring(p + 1);
                                  }
                                  return cookieData[k]
                              })();
                      },
                      /*删除本地存储数据*/
                      clearLocalData: function (k) {
                          k = k || "localDataTemp";
                          typeof window.localStorage !== "undefined" ? localStorage.removeItem(k) :
                              (function () {
                                  document.cookie = k + "=temp" + ";max-age=0";
                              })()
                      },
                      init:function(){
                          this.bindEvent();
                      },
                      //事件绑定
                      bindEvent:function(){
                          var _this=this;
                          win.addEventListener("message",function(evt){
                              if(win.parent!=evt.source){return}
                              var options=JSON.parse(evt.data);
                              if(options.type=="GET"){
                                  var data=tools.getLocalCookie(options.key);
                                  win.parent.postMessage(data, "*");
                              }
                              options.type=="SET"&&_this.setLocalCookie(options.key,options.value);
                              options.type=="REM"&&_this.clearLocalData(options.key);
                          },false)
                      }
                  };
                  var tools=new fn();
                  tools.init();
              })(document,window);
              /**CURD本地存储信息 end**/
    
    • 前端页面js代码(客户端):
            //页面初始化向iframe域名发送消息
            window.onload = function() {
                console.log('get key value......................')
                window.frames[0].postMessage(JSON.stringify({type:"GET",key:"User-Token"}),'*');
            }
            //监听message信息,接收从iframe域下获取到的token信息,然后存储到localstorage或cookie中
            window.addEventListener('message', function(e) {
                console.log('listen.....');
                var data = e.data;
                console.log(data);
                if(data != null){
                    localStorage.setItem("User-Token", data);
                }
            }, false);
    

    总结:
    优点:在非跨域环境下使用JWT机制是一个非常不错的选择,实现方式简单,操作方便,能够快速实现。由于服务端不存储用户状态信息,因此大用户量,对后台服务也不会造成压力;
    缺点:跨域实现相对比较麻烦,安全性也有待探讨。因为JWT令牌返回到页面中,可以使用js获取到,如果遇到XSS攻击令牌可能会被盗取,在JWT还没超时的情况下,就会被获取到敏感数据信息。

    展开全文
  • Jwt简介

    2019-05-24 21:24:03
     JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字...

     JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。
    一、跨域认证的问题
     HTTP协议是无状态的,也就是说,如果我们已经认证了一个用户,那么他下一次请求的时候,服务器不知道我是谁,我们必须再次认证。
     互联网服务离不开用户认证。一般流程是下面这样。

    1、用户向服务器发送用户名和密码。
    2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
    3、服务器向用户返回一个 session_id,写入用户的 Cookie。
    4、用户随后的每一次请求,都会通过 Cookie,将session_id 传回服务器。
    5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

     这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。
     举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?
     一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。
     另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

    二、JWT 的原理
    JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

    {
      “姓名”: “张三”,
     “角色”: “管理员”,
     “到期时间”: “2018年7月1日0点0分”
    }

     以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

    三、JWT 的数据结构
     实际的 JWT 大概就像下面这样。

    eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

     它是一个很长的字符串,中间用点(.)分隔成三个部分。
     JWT 的三个部分依次如下。

    Header(头部)
    Payload(负载)
    Signature(签名)

    写成一行,就是下面的样子。

    Header.Payload.Signature

    下面依次介绍这三个部分。

    1、 Header
    Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。

    {
      “alg”: “HS256”,
      “typ”: “JWT”
    }

     上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
     最后,将上面的 JSON 对象使用 Base64URL 算法(详见后文)转成字符串。
    2、Payload
     Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

    iss (issuer):签发人
    exp (expiration time):过期时间
    sub (subject):主题 aud
    (audience):受众
    nbf (Not Before):生效时间
    iat (Issued At):签发时间
    jti (JWT ID):编号

    除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。

    {
     “sub”: “1234567890”,
     “name”: “John Doe”,
     “admin”: true
    }

     注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
     这个 JSON 对象也要使用 Base64URL 算法转成字符串。
    3、 Signature
     Signature 部分是对前两部分的签名,防止数据篡改。
     首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

    HMACSHA256(
     base64UrlEncode(header) + “.” +
     base64UrlEncode(payload),
     secret)

     算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
    4、Base64URL
     前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。
     JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。 Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

    四、JWT 的使用方式
     在认证的时候,当用户用他们的凭证成功登录以后,一个JSON Web Token将会被返回。此后,token就是用户凭证了,你必须非常小心以防止出现安全问题。一般而言,你保存令牌的时候不应该超过你所需要它的时间。
     客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。
     此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面,因为它不使用cookie。

    Authorization: Bearer token

     另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。

    五、JWT 的几个特点:
     1、JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
     2、JWT 不加密的情况下,不能将私密数据写入 JWT。
     3、JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
     4、JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
     5、JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
     6、为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

    六、 JWT与Session的差异
     1、相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。
     2、Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。
     3、而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。
     4、Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

    七、基于Token的身份认证流程
     基于Token的身份认证是无状态的,服务器或者Session中不会存储任何用户信息。没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。
     虽然这一实现可能会有所不同,但其主要流程如下:

    1、用户携带用户名和密码请求访问
    2、服务器校验用户凭据
    3、应用提供一个token给客户端
    4、客户端存储token,并且在随后的每一次请求中都带着它
    5、 服务器校验token并返回数据

    注意:
     每一次请求都需要token。Token应该放在请求header中。

    八、使用Token的优点
     1、无状态和可扩展性:Tokens存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。
     2、安全:Token不是Cookie。(The token, not a cookie.)每次请求的时候Token都会被发送。而且,由于没有Cookie被发送,还有助于防止CSRF攻击。即使在你的实现中将token存储到客户端的Cookie中,这个Cookie也只是一种存储机制,而非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话。
     3、token在一段时间以后会过期,这个时候用户需要重新登录。这有助于我们保持安全。还有一个概念叫token撤销,它允许我们根据相同的授权许可使特定的token甚至一组token无效。

    九、JWT与OAuth的区别
     1、OAuth2是一种授权框架 ,JWT是一种认证协议。
     2、无论使用哪种方式切记用HTTPS来保证数据的安全性
     3、OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。

    展开全文
  • JWT 完整使用详解

    千次阅读 2019-05-29 14:49:43
    JWT全称JSON Web Tokens,是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。它的两大使用场景是:认证和数据交换。 一、安装之前 资料 先摆出几个参考资料,可以把连接都...

     

    JWT 全称 JSON Web Tokens ,是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。它的两大使用场景是:认证和数据交换。

    一、安装之前

    资料

    先摆出几个参考资料,可以把连接都打开,方便查阅:

    二、安装及基础配置

    Laravel

    1. 使用 composer 安装

    # 建议使用1.0以上版本 ,laravel5.4及以下安装rc.1版
    composer require tymon/jwt-auth "1.0.0-rc.1"
    
    # 建议使用1.0以上版本 laravel5.4及以上安装rc.3版
    composer require tymon/jwt-auth "1.0.0-rc.3"

    2. 进行一些配置

    这里指的注意的是,有些文档会说要添加 Tymon\JWTAuth\Providers\LaravelServiceProvider::class ,这只在 Laravel 5.4 及以下版本是必要的,更新的 Laravel 版本无需添加。

    还有一些文档说要添加 Tymon\JWTAuth\Providers\JWTAuthServiceProvider 这是很久以前的 JWT 版本的(大概 0.5.3 以前的版本)。

    2.1 发布配置文件

    # 这条命令会在 config 下增加一个 jwt.php 的配置文件
    php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

    2.2 生成加密密钥

    # 这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar
    php artisan jwt:secret

    2.3 更新你的模型

    如果你使用默认的 User 表来生成 token,你需要在该模型下增加一段代码

    <?php
    
    namespace App;
    
    use Tymon\JWTAuth\Contracts\JWTSubject;
    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    
    class User extends Authenticatable implements JWTSubject    # 这里别忘了加
    {
        use Notifiable;
    
        // Rest omitted for brevity
    
        /**
         * Get the identifier that will be stored in the subject claim of the JWT.
         *
         * @return mixed
         */
        public function getJWTIdentifier()
        {
            return $this->getKey();
        }
    
        /**
         * Return a key value array, containing any custom claims to be added to the JWT.
         *
         * @return array
         */
        public function getJWTCustomClaims()
        {
            return [];
        }
    }

    2.4 注册两个 Facade

    这两个 Facade 并不是必须的,但是使用它们会给你的代码编写带来一点便利。

    config/app.php

    'aliases' => [
            ...
            // 添加以下两行
            'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth',
            'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory',
    ],

    如果你不使用这两个 Facade,你可以使用辅助函数 auth ()

    auth () 是一个辅助函数,返回一个 guard,暂时可以看成 Auth Facade。

    对于它有很多有必要说的,可以看我单独写的一篇文章 ——Laravel 辅助函数 auth 与 JWT 扩展详解

    // 如果你不用 Facade,你可以这么写
    auth('api')->refresh();
    // 用 JWTAuth Facade
    JWTAuth::parseToken()->refresh();

    两个 Facede 常用可使用方法,可以看文章后面的附录。

    2.5 修改 auth.php

    config/auth.php

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
    
        'api' => [
            'driver' => 'jwt',      // 原来是 token 改成jwt
            'provider' => 'users',
        ],
    ],

    2.6 注册一些路由

    注意:在 Laravel 下,route/api.php 中的路由默认都有前缀 api 。

    Route::group([
    
        'prefix' => 'auth'
    
    ], function ($router) {
    
        Route::post('login', 'AuthController@login');
        Route::post('logout', 'AuthController@logout');
        Route::post('refresh', 'AuthController@refresh');
        Route::post('me', 'AuthController@me');
    
    });

    2.7 创建 token 控制器

    php artisan make:controller AuthController

    AuthController

    值得注意的是 Laravel 这要用 auth('api') ,至于为什么,我另一篇关于 JWT 扩展详解的文章里有讲。

    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Support\Facades\Auth;
    use App\Http\Controllers\Controller;
    
    class AuthController extends Controller
    {
        /**
         * Create a new AuthController instance.
         * 要求附带email和password(数据来源users表)
         * 
         * @return void
         */
        public function __construct()
        {
            // 这里额外注意了:官方文档样例中只除外了『login』
            // 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新
            // 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新
            // 不过刷新一次作废
            $this->middleware('auth:api', ['except' => ['login']]);
            // 另外关于上面的中间件,官方文档写的是『auth:api』
            // 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
        }
    
        /**
         * Get a JWT via given credentials.
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function login()
        {
            $credentials = request(['email', 'password']);
    
            if (! $token = auth('api')->attempt($credentials)) {
                return response()->json(['error' => 'Unauthorized'], 401);
            }
    
            return $this->respondWithToken($token);
        }
    
        /**
         * Get the authenticated User.
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function me()
        {
            return response()->json(auth('api')->user());
        }
    
        /**
         * Log the user out (Invalidate the token).
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function logout()
        {
            auth('api')->logout();
    
            return response()->json(['message' => 'Successfully logged out']);
        }
    
        /**
         * Refresh a token.
         * 刷新token,如果开启黑名单,以前的token便会失效。
         * 值得注意的是用上面的getToken再获取一次Token并不算做刷新,两次获得的Token是并行的,即两个都可用。
         * @return \Illuminate\Http\JsonResponse
         */
        public function refresh()
        {
            return $this->respondWithToken(auth('api')->refresh());
        }
    
        /**
         * Get the token array structure.
         *
         * @param  string $token
         *
         * @return \Illuminate\Http\JsonResponse
         */
        protected function respondWithToken($token)
        {
            return response()->json([
                'access_token' => $token,
                'token_type' => 'bearer',
                'expires_in' => auth('api')->factory()->getTTL() * 60
            ]);
        }
    }

    Lumen

    1. 使用 composer 安装

    上面是用命令行安装的,这里用 composer.json 安装。

    // 我当时可用的版本是这个
    "tymon/jwt-auth": "1.*@rc"

    执行

    composer update

    2. 进行一些配置

    2.1 开启 Facade 和 Eloquent

    取消以下行的注释。

    bootstrap/app.php

    // $app->withFacades();
    
    // $app->withEloquent();

    2.2 开启中间件认证

    取消以下行的注释。

    bootstrap/app.php

    // $app->routeMiddleware([
    //     'auth' => App\Http\Middleware\Authenticate::class,
    // ]);
    
    // $app->register(App\Providers\AuthServiceProvider::class);

    2.3 添加服务提供者

    bootstrap/app.php

    // 有些文档里是说添加 Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,那是旧版本的
    $app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);

    2.4 生成加密密钥

    这条命令会在 .env 文件下生成一个加密密钥,如:JWT_SECRET=foobar

    php artisan jwt:secret

    2.5 更新你的模型

    如果你使用默认的 User 表来生成 token,你需要在该模型下增加一段代码

    <?php
    
    namespace App;
    
    use Illuminate\Auth\Authenticatable;
    use Laravel\Lumen\Auth\Authorizable;
    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
    use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
    use Tymon\JWTAuth\Contracts\JWTSubject;
    
    class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
    {
        use Authenticatable, Authorizable;
    
        ...
    
        /**
         * Get the identifier that will be stored in the subject claim of the JWT.
         *
         * @return mixed
         */
        public function getJWTIdentifier()
        {
            return $this->getKey();
        }
    
        /**
         * Return a key value array, containing any custom claims to be added to the JWT.
         *
         * @return array
         */
        public function getJWTCustomClaims()
        {
            return [];
        }
    }

    2.6 注册两个 Facade

    Lumen 中没有辅助函数 auth (),这两个 Facade 就挺有用了。

    bootstrap/app.php

    把原先去了注释的那一行再改一下。

    $app->withFacades(true, [
        'Tymon\JWTAuth\Facades\JWTAuth' => 'JWTAuth',
        'Tymon\JWTAuth\Facades\JWTFactory' => 'JWTFactory',
    ]);

    2.7 设置 auth.php

    把 \vendor\laravel\lumen-framework\config\auth.php 也复制到 项目根目录 config 文件夹(没有就新建)。

    文档中有提到 Lumen 风格的配置文件这个概念

    指的就是都在 .env 文件中设置各种设置项,在 \vendor\laravel\lumen-framework\config 文件夹下面的其他配置文件中也可以看到,很多配置项都有 env(设置项key, 默认值) 这个方法,有这个配置项的就可以在 .env 文件中设置 设置项=你的设置值 这样设置。

    而复制到根目录 config 文件夹是 Laravel 风格的配置文件实现方式

    这里我本来想尽量按 Lumen 风格实现的,但是下面这些属性默认并没有 env() 方式实现,所以我还是复制到根目录下改算了。

    auth.php

    按下面进行添加或修改。

    'guards' => [
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],
    
    ...
    
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => \App\User::class
    ]

    2.8 注册一些路由

    Route::group([
    
        'prefix' => 'auth'
    
    ], function ($router) {
    
        Route::post('login', 'AuthController@login');
        Route::post('logout', 'AuthController@logout');
        Route::post('refresh', 'AuthController@refresh');
        Route::post('me', 'AuthController@me');
    
    });

    2.9 创建 token 控制器

    Lumen 还精简了很多辅助函数,比如 auth 和 bcrypt 等。

    可以安装 cosmicvelocity/lumen-helpers 或 albertcht/lumen-helpers 补全(建议用后者,更好安装)

    或者使用上面说的两个 Facade。

    AuthController.php

    如果你没使用扩展补充的辅助函数,你需要这么写,不然直接用上面的 Larvavel 那个

    <?php
    
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use Tymon\JWTAuth\Facades\JWTAuth;
    
    class AuthController extends Controller
    {
        /**
         * Create a new AuthController instance.
         *
         * @return void
         */
        public function __construct()
        {
            // 这里额外注意了:官方文档样例中只除外了『login』
            // 这样的结果是,token 只能在有效期以内进行刷新,过期无法刷新
            // 如果把 refresh 也放进去,token 即使过期但仍在刷新期以内也可刷新
            // 不过刷新一次作废
            $this->middleware('auth:api', ['except' => ['login']]);
            // 另外关于上面的中间件,官方文档写的是『auth:api』
            // 但是我推荐用 『jwt.auth』,效果是一样的,但是有更加丰富的报错信息返回
        }
    
        /**
         * Get a JWT via given credentials.
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function login(Request $request)
        {
            $credentials = $request->only('email', 'password');
    
            if (! $token = JWTAuth::attempt($credentials)) {
                return response()->json(['error' => 'Unauthorized'], 401);
            }
    
            return $this->respondWithToken($token);
        }
    
        /**
         * Get the authenticated User.
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function me()
        {
            return response()->json(JWTAuth::parseToken()->touser());
        }
    
        /**
         * Log the user out (Invalidate the token).
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function logout()
        {
            JWTAuth::parseToken()->invalidate();
    
            return response()->json(['message' => 'Successfully logged out']);
        }
    
        /**
         * Refresh a token.
         *
         * @return \Illuminate\Http\JsonResponse
         */
        public function refresh()
        {
            return $this->respondWithToken(JWTAuth::parseToken()->refresh());
        }
    
        /**
         * Get the token array structure.
         *
         * @param  string $token
         *
         * @return \Illuminate\Http\JsonResponse
         */
        protected function respondWithToken($token)
        {
            return response()->json([
                'access_token' => $token,
                'token_type' => 'bearer',
                'expires_in' => JWTAuth::factory()->getTTL() * 60
            ]);
        }
    }

    关于中间件

    1.0 版本以上的 jwt-auth,中间件在服务提供者中已经定义了,所以不需要额外写,按上面来即可。

    下面是可用的中间件,第一二个功能一样,但是第二个不会抛出错误,第三四个功能一样,没什么区别。

    tymon\jwt-auth\src\Providers\AbstractServiceProvider.php

    protected $middlewareAliases = [
        'jwt.auth' => Authenticate::class,
        'jwt.check' => Check::class,
        'jwt.refresh' => RefreshToken::class,
        'jwt.renew' => AuthenticateAndRenew::class,
    ];

    三、JWT Token 详解

    1. token 的获取、使用、删除和刷新

    • 以下用 postman 演示,{{TEST}} 为 postman 全局变量:test.yfree.ccc
    • Laravel 环境下写在 api.php 中的路由默认有前缀 api
    • 下面的图是 Lumen 环境的,没有默认区前缀 api

    1.1 获取 token

    获取 token

    获取token

    1.2 使用 token

    有两种使用方法:

    • 加到 url 中:?token=你的token
    • 加到 header 中,建议用这种,因为在 https 情况下更安全:Authorization:Bearer 你的token

    使用 token

    使用 token

    添加中间件保护的就需要使用 token 进行访问

    可以使用的中间件有 auth、auth:api、jwt.auth、jwt.refresh、jwt.check、jwt.renew

    关于这些中间件之间有什么差别,可以看我的另一篇文章:Laravel 辅助函数 auth 与 JWT 扩展详解

    1.3 删除 token

    删除 token

     

    删除 token 后,token 就会失效,无法再利用其获取数据。

    1.4 刷新 token

    刷新 token

    刷新 token

    刷新后,旧 token 将会失效,但是你可以设置一个宽限时间,这个在后面具体说。

    2. token 的组成、创建以及解析

    2.1 组成

    一个 JWT token 是一个字符串,它由三部分组成,头部、载荷与签名,中间用 . 分隔,例如:xxxxx.yyyyy.zzzzz

    头部(header)

    头部通常由两部分组成:令牌的类型(即 JWT)和正在使用的签名算法(如 HMAC SHA256 或 RSA.)。
    例如:

    {
      "alg": "HS256",
      "typ": "JWT"
    }

    然后用 Base64Url 编码得到头部,即 xxxxx

    载荷(Payload)

    载荷中放置了 token 的一些基本信息,以帮助接受它的服务器来理解这个 token。同时还可以包含一些自定义的信息,用户信息交换。

    载荷的属性也分三类:

    • 预定义(Registered)
    • 公有(public)
    • 私有(private)

    预定义的载荷

    {
      "sub": "1",
      "iss": "http://localhost:8000/auth/login",
      "iat": 1451888119,
      "exp": 1454516119,
      "nbf": 1451888119,
      "jti": "37c107e4609ddbcc9c096ea5ee76c667",
      "aud": "dev"
    }

    这里面的前 7 个字段都是由官方所定义的,也就是预定义(Registered claims)的,并不都是必需的。

    • iss (issuer):签发人
    • sub (subject):主题
    • aud (audience):受众
    • exp (expiration time):过期时间
    • nbf (Not Before):生效时间,在此之前是无效的
    • iat (Issued At):签发时间
    • jti (JWT ID):编号

    公有的载荷

    在使用 JWT 时可以额外定义的载荷。为了避免冲突,应该使用 IANA JSON Web Token Registry 中定义好的,或者给额外载荷加上类似命名空间的唯一标识。

    私有载荷

    在信息交互的双方之间约定好的,既不是预定义载荷也不是公有载荷的一类载荷。这一类载荷可能会发生冲突,所以应该谨慎使用。

    将上面的 json 进行 Base64Url 编码得到载荷,,即 yyyyy

    关于载荷的理解:

    这里三种载荷的定义应该明确的一点是 —— 对于后两种载荷,它并非定义了载荷的种类,然后让你去选用哪种载荷,而是对你可能会定义出来的载荷做一个分类。

    比如你定义了一个 admin 载荷,这个载荷按其分类应该是私有载荷,可能会和其他人定义的发生冲突。但如果你加了一个前缀(命名空间),如 namespace-admin,那么这应该就算一个公有载荷了。(但其实标准并没有定义怎么去声明命名空间,所以严格来说,还是可能会冲突)

    但是在现实中,团队都是约定好的了要使用的载荷,这样的话,好像根本不存在冲突的可能。那为什么文档要这么定义呢?我的理解是,RFC 是提出一种技术规范,出发点是一套通用的规范,考虑的范围是所有开发者,而不仅仅局限于一个开发者团队。就像用 token 做认证已经是很常见的技术了,但是 JWT 的提出就相当于提出了一套较为通用的技术规范。既然是为了通用,那么考虑在大环境下的冲突可能性也是必须的。

    签名(Signature)

    签名时需要用到前面编码过的两个字符串,如果以 HMACSHA256 加密,就如下:

    HMACSHA256(
        base64UrlEncode(header) + "." +
        base64UrlEncode(payload),
        secret
    )

    加密后再进行 base64url 编码最后得到的字符串就是 token 的第三部分 zzzzz

    组合便可以得到 token:xxxxx.yyyyy.zzzzz

    签名的作用:保证 JWT 没有被篡改过,原理如下:

    HMAC 算法是不可逆算法,类似 MD5 和 hash ,但多一个密钥,密钥(即上面的 secret)由服务端持有,客户端把 token 发给服务端后,服务端可以把其中的头部和载荷再加上事先共享的 secret 再进行一次 HMAC 加密,得到的结果和 token 的第三段进行对比,如果一样则表明数据没有被篡改。

    Hash-based Message Authentication Code

    PHP 代码示例

    // 这里要开启true
    $zzzzz = $this->base64url_encode(hash_hmac('sha256', 'xxxxx.yyyyy', getenv('JWT_SECRET'), true));
    
    protected function base64url_encode($data) {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    2.2 token 的创建

    前面的 AuthController.php 中有两行展现了这一种 token 的创建方法,即用用户所给的账号和密码进行尝试,密码正确则用对应的 User 信息返回一个 token 。

    但 token 的创建方法不止这一种,接下来介绍 token 的三种创建方法:

    • 基于账密参数
    • 基于 users 模型返回的实例
    • 基于 users 模型中的用户主键 id

    a) 基于账密参数

    这就是刚刚说的哪一种,贴出具体代码。

    // 使用辅助函数
    $credentials = request(['email', 'password']); 
    $token = auth()->attempt($credentials)
    
    // 使用 Facade
    $credentials = $request->only('email', 'password');
    $token = JWTAuth::attempt($credentials);

    b) 基于 users 模型返回的实例

    // 使用辅助函数
    $user = User::first();
    $token = auth()->login($user);
    
    // 使用 Facade
    $user = User::first();
    $token = JWTAuth::fromUser($credentials);

    c) 基于 users 模型中的主键 id

    // 使用辅助函数
    $token = auth()->tokenById(1);
    
    // 使用 Facade
    源码中没找到

    2.3 token 的解析

    a) 解析 token 到对象

    只有 Facade 需要这样。

    // 把请求发送过来的直接解析到对象
    JWTAuth::parseToken();

    b) 获取 token 中的 user 信息

    // 辅助函数
    $user = auth()->user();
    
    // Facade
    $user = JWTAuth::parseToken()->authenticate();

    c) 获取 token

    如果 token 被设置则会返回,否则会尝试使用方法从请求中解析 token ,如果 token 未被设置或不能解析最终返回 false。

    // 辅助函数
    $token = auth()->getToken();
    
    // Facade
    $token = JWTAuth::parseToken()->getToken();

    更多方法可以看文章后面的附录。

    d) 如果是前端

    直接 base64 解码 token 的前两段即可以知道所需的信息。

    3. 载荷的设置和获取

    a) 载荷设置

    载荷信息会在 token 解码时得到,同时越大的数组会生成越长的 token ,所以不建议放太多的数据。同时因为载荷是用 Base64Url 编码,所以相当于明文,因此绝对不能放密码等敏感信息。

    $customClaims = ['foo' => 'bar', 'baz' => 'bob'];
    
    // 辅助函数
    $token = auth()->claims($customClaims)->attempt($credentials);
    
    // Facade - 1
    $token = JWTAuth::claims($customClaims)->attempt($credentials);
    
    --- 下面两种试了好像不行,不过前面的够用了
    
    // Facade - 2
    $payload = JWTFactory::make($customClaims);
    $token = JWTAuth::encode($payload);
    
    // Facade - 3
    $payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();
    $token = JWTAuth::encode($payload);

    b) 载荷解析

    从请求中把载荷解析出来。可以去看扩展源代码,里面还有很多的方法。

    // 辅助函数
    $exp = auth()->payload()->get('exp');
    $json = auth()->payload()->toJson();
    $array = auth()->payload()->jsonSerialize();
    $sub = $array['sub'];
    
    // Facade - 1
    $payload = JWTAuth::parseToken()->getPayload();
    $payload->get('sub'); // = 123
    $payload['jti']; // = 'asfe4fq434asdf'
    $payload('exp') // = 123456
    $payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc
    
    // Facade - 2
    $exp = JWTAuth::parseToken()->getClaim('exp');

    4. token 的三个时间

    一个 token 一般来说有三个时间属性,其配置都在 config/jwt.php 内。

    有效时间

    有效时间指的的是你获得 token 后,在多少时间内可以凭这个 token 去获取内容,逾时无效。

    // 单位:分钟
    'ttl' => env('JWT_TTL', 60)

    刷新时间

    刷新时间指的是在这个时间内可以凭旧 token 换取一个新 token。例如 token 有效时间为 60 分钟,刷新时间为 20160 分钟,在 60 分钟内可以通过这个 token 获取新 token,但是超过 60 分钟是不可以的,然后你可以一直循环获取,直到总时间超过 20160 分钟,不能再获取。

    这里过期后能否刷新,经 @Rootrl 指出,其实并不是这么绝对,具体细节,看我们上面 AuthController 处的代码。有详细补充
    这也是一个 token 被加入黑名单之后,会存在的时间

    // 单位:分钟
    'refresh_ttl' => env('JWT_REFRESH_TTL', 20160)

    宽限时间

    宽限时间是为了解决并发请求的问题,假如宽限时间为 0s ,那么在新旧 token 交接的时候,并发请求就会出错,所以需要设定一个宽限时间,在宽限时间内,旧 token 仍然能够正常使用。

    // 宽限时间需要开启黑名单(默认是开启的),黑名单保证过期token不可再用,最好打开
    'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true)
    
    // 设定宽限时间,单位:秒
    'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 60)

    5. 关于 JWT 的讨论

    5.1 为什么用 JWT?

    看我的新文章 JWT 超详细分析 。

    5.2 token 的刷新问题?

    a) token 为什么要刷新吗?

    首先 Basic Auth 是一种最简单的认证方法,但是由于每次请求都带用户名和密码,频繁的传输肯定不安全,所以才有 cookies 和 session 的运用。如果 token 不刷新,那么 token 就相当于上面的用户名 + 密码,只要获取到了,就可以一直盗用,因此 token 设置有效期并能够进行刷新是必要的。

    b) token 有效期多久合适,刷新频率多久合适?

    有效期越长,风险性越高,有效性越短,刷新频率越高,刷新就会存在刷新开销,所以这需要综合考虑。而且 web 端应该设置为分钟级和小时级,而移动端应该设置为天级和周级。

    c) 有没有必要每次都刷新 token ?

    看我的新文章 JWT 超详细分析 。

    四、附录

    1. JWT 的 两个 Facade

    1.1 JWTAuth

    JWTAuth::parseToken()->方法() 一般都可以换成 auth()->方法()

    token 生成

    attempt

    根据 user 账密新建一个 token。

    $credentials = $request->only('email', 'password');
    $token = JWTAuth::attempt($credentials);

    fromUser or fromSubject

    根据 user 对象生成一个 token。后者是前者别名。

    $user = User::find(1);
    $token = JWTAuth::fromUser($user);

    token 控制

    refresh

    更新 token。

    $newToken = JWTAuth::parseToken()->refresh();

    invalidate

    让一个 token 无效。

    JWTAuth::parseToken()->invalidate();

    check

    检验 token 的有效性。

    if(JWTAuth::parseToken()->check()) {
        dd("token是有效的");
    }

    token 解析

    authenticate or toUser or user

    这三个效果是一样的,toUser 是 authenticate 的别名,而 user 比前两者少一个 user id 的校验,但并没有什么影响。

    $user = JWTAuth::parseToken()->toUser();

    parseToken

    从 request 中解析 token 到对象中,以便进行下一步操作。

    JWTAuth::parseToken();

    getToken

    从 request 中获取 token。

    $token = JWTAuth::getToken();  // 这个不用 parseToken ,因为方法内部会自动执行一次

    载荷控制

    customClaims or claims

    设置载荷的 customClaims 部分。后者是前者的别名。

    $customClaims = ['sid' => $sid, 'code' => $code];
    $credentials = $request->only('email', 'password');
    $token = JWTAuth::customClaims($customClaims)->attempt($credentials);

    getCustomClaims

    获取载荷的 customClaims 部分,返回一个数组。

    $customClaims = JWTAuth::parseToken()->getCustomClaims()

    getPayload or payload

    获取所有载荷,三个都是一样的,最后一个一般用来检验 token 的有效性

    $payload = JWTAuth::parseToken()->payload();
    
    // then you can access the claims directly e.g.
    $payload->get('sub'); // = 123
    $payload['jti']; // = 'asfe4fq434asdf'
    $payload('exp') // = 123456
    $payload->toArray(); // = ['sub' => 123, 'exp' => 123456, 'jti' => 'asfe4fq434asdf'] etc

    getClaim

    获取载荷中指定的一个元素。

    $sub = JWTAuth::parseToken()->getClaim('sub');

    1.2 JWTGuard

    这个 Facade 主要进行载荷的管理,返回一个载荷对象,然后可以通过 JWTAuth 来对其生成一个 token。

    // 载荷的高度自定义
    $payload = JWTFactory::sub(123)->aud('foo')->foo(['bar' => 'baz'])->make();
    $token = JWTAuth::encode($payload);
    $customClaims = ['foo' => 'bar', 'baz' => 'bob'];
    $payload = JWTFactory::make($customClaims);
    $token = JWTAuth::encode($payload);

    1.3 其他一些用法

    这里用 auth 的写法,因为 Laravel 有多个 guard,默认 guard 也不是 api ,所以需要写成 auth('api') 否则,auth() 即可。

    设置载荷

    $token = auth('api')->claims(['foo' => 'bar'])->attempt($credentials);

    显示设置 token

    $user = auth('api')->setToken('eyJhb...')->user();

    显示设置请求

    $user = auth('api')->setRequest($request)->user();

    重写有效时间

    $token = auth('api')->setTTL(7200)->attempt($credentials);

    验证账密是否正确

    $boolean = auth('api')->validate($credentials);

    原文:https://learnku.com/articles/10885/full-use-of-jwt 

    展开全文
  • spring security oauth2 改进 使用JWT认证 接着上一篇spring security oauth2 入门,之后网上查阅了一些资料,准备对oauth2修改使用jwt的模式认证,使用jwt有两种认证方式1,对称加密方式。2,非对称加密方式,...
  • Jwt与RSA非对称加密

    千次阅读 2019-06-05 21:33:46
    Jwt与RSA加密1. 无状态登录原理1.1 什么是有状态?1.2 什么是无状态1.3 如何实现无状态1.4 JWT1.4.1 简介1.4.2 数据格式1.4.3 JWT交互流程1.4.4 非对称加密1.5 结合Zuul的鉴权流程1.5.1 没有RSA加密时1.5.2 结合RSA...
  • 文章目录1 有状态登录2 无状态登陆3 如何实现无状态4 JWT4.1 数据格式4.2 JWT交互流程5 RSA加密5.1 加密技术5.1没有RSA加密时5.2 结合RSA的鉴权 目前我们创建了若干个微服务,假如我们在用户中心做了登陆,接下来要...
  • Jwt+RSA非对称加密

    2019-11-07 10:45:02
    登录我们分为有状态登录以及无状态登录. 有状态登录:服务端需要记录每次回话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session. 例如:用户登录后,我们把登陆者的信息...
  • JWT&对称加密&非对称加密

    千次阅读 2020-03-01 20:17:50
    JWT JWT原理图: JWT的数据结构 jwt头:JWT头部分是一个描述JWT元数据的JSON对象 有效载荷:七个默认字段+自定义私有字段 签名=HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret ...
  • JWT全面解读、使用步骤

    万次阅读 多人点赞 2018-06-27 15:13:28
    JWT全面解读 JWT全面解读 前言 JWT基本使用 在pom.xml引入java-jwt 示例如下 概念介绍 JWT消息构成 头部 playload 标准中注册的声明 (建议但不强制使用) 自定义数据 签名signature JJWT 引入 使用方法 ...
  • JWT

    千次阅读 2019-04-23 14:42:12
    10分钟了解JSON Web令牌(JWT) JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。今天给大家介绍JWT的原理和用法。 1.跨域身份验证 Internet服务无法与用户身份验证分开。一般过程如下。 1....
  • jwt

    千次阅读 2019-08-17 22:19:01
    文章目录介绍JWT的验证过程实例 在之前的项目中,大部分网页是通过session来判断用户是否有权限来访问个别网页的,但是如果是SPA项目的话,前后端分离,客户请求的是前端,所以并不会保存数据到session,那该怎么办...
  • JWT(JSON Web Token)原理简介

    万次阅读 2018-10-07 21:36:05
    参考了一下这篇文章:https://medium.com/vandium-software/5-easy-steps-to-understanding-json-web-tokens-jwt-1164c0adfcec 原理说的非常清楚。总结如下: 首先这个先说这个东西是什么,干什么用的,一句话说:...
  • Jwt

    2019-09-05 16:10:09
    1. JWT是什么 JSON Web Token (JWT),它是目前最流行的跨域身份验证解决方案 JWT的精髓在于:“去中心化”,数据是保存在客户端的。 2. JWT的工作原理 1、是在服务器身份验证之后,将生成一个JSON对象并将其发送回...
  • (精华)2020年6月26日 C#类库 JWT帮助类

    万次阅读 2020-06-26 14:34:49
    using Newtonsoft.Json.Linq; namespace Core.Util ... private static readonly string _headerBase64Url = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}".Base64UrlEncode(); public static readonly string JWTSecret =
  • // nodejs+express+jwt-simple let jwt = require('jwt-simple'); //秘钥 let secret = "laney"; let time = 10; let tokenExpiresTime = 1000 * 60 * 60 * 24 * 7; //token过期时间,毫秒为单位, 7天 module....
  • Go实战--golang中使用JWT(JSON Web Token)

    万次阅读 多人点赞 2019-07-18 19:33:38
    生命不止,继续 go go go !!!之前写过关于golang中如何使用cookie的博客: 实战–go中使用cookie今天就来跟大家简单介绍一下golang中如何使用token,当然是要依赖一下github上的优秀的开源库了。...
  • Spring Boot+Spring Security+JWT 实现 RESTful Api 认证 (一)

    万次阅读 多人点赞 2017-09-13 14:39:51
    现在我们来看如何利用JWT技术为API增加授权保护,保证只有获得授权的用户才能够访问API。 一:开发一个简单的API 在IDEA开发工具中新建一个maven工程,添加对应的依赖如下: <dependency> <gro...
  • GitHub地址:https://github.com/zhang-xiaoxiang/shiro-jwt 说明:由于初衷是解决自己项目的bug的,就找的网上的一面博客瞎搞了一个demo.然后报的错网上难以找到解决办法,后来自己解决了,就记录一下,所以不算教程,我...
1 2 3 4 5 ... 20
收藏数 43,970
精华内容 17,588
关键字:

jwt