token 订阅
在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。 展开全文
在计算机身份认证中是令牌(临时)的意思,在词法分析中是标记的意思。一般作为邀请、登录系统使用。
信息
学    科
信息技术(IT)
外文名
Token
词    性
名词
中文名
令牌,标记
Token令牌
(信息安全术语)Token, 令牌,代表执行某些操作的权利的对象访问令牌(Access token)表示访问控制操作主体的系统对象邀请码,在邀请系统中使用Token, Petri 网(Petri net)理论中的Token密保令牌(Security token),或者硬件令牌,例如U盾,或者叫做认证令牌或者加密令牌,一种计算机身份校验的物理设备会话令牌(Session token),交互会话中唯一身份标识符令牌化技术 (Tokenization), 取代敏感信息条目的处理过程
收起全文
精华内容
参与话题
问答
  • token

    千次阅读 2019-07-24 17:28:40
    token 身份验证 http 请求的无状态性 JWT (jsonwebtoken) 用户登录 服务器端产生一个token (加密字符串) 发送给前端 前端将token 进行保存 前端发起数据请求的时候携带token 服务端 验证token 是否合法 如果...

    token

    • 身份验证
      http 请求的无状态性

    • JWT (jsonwebtoken)

    1. 用户登录 服务器端产生一个token (加密字符串) 发送给前端
    2. 前端将token 进行保存
    3. 前端发起数据请求的时候携带token
    4. 服务端 验证token 是否合法 如果合法继续操作 不合法终止操作
    5. token 的使用场景 无状态请求 保持用户的登录状态 第三方登录(token+auth2.0)
    • 小案例:登录页面
    1. 先打开终端,运行下面代码:
      在这里插入图片描述
    2. cd 进入token_demo,再安装依赖性文件:
      在这里插入图片描述
    3. 打造一个简单的接口文件(login.js):
      在这里插入图片描述
    4. 在app.js 中引入 路由模块 :

    在这里插入图片描述
    在这里插入图片描述
    5. 在 views 文件夹中新增一个 login.ejs 文件:
    在这里插入图片描述
    6. 启动项目( npm run start ), 测试接口:
    在这里插入图片描述
    7. 新建前端文件 token_fe,新建 index.html,样式布局。

    <body onload="loadFn()">
      <div class="container">
        <div class="row">
          <div class="form-group">
            <label for=""> 用户名 </label>
            <input type="text" class="form-control username" name="" id="" aria-describedby="emailHelpId" placeholder="">
          </div>
        </div>
        <div class="row">
          <div class="form-group">
            <label for=""> 密码 </label>
            <input type="password" class="form-control password" name="" id="" placeholder="">
          </div>
        </div>
        <div class="row">
          <button type="button" class="btn btn-primary login"> 登录 </button>
        </div>
      </div>
    </body>
    
    1. (通过go live)打开静态服务器
    2. 点击登录得到用户名和密码,发送请求:
      在这里插入图片描述
    3. 添加 onload 事件, 通过判断 cookie或是 本地存储是否有token值。如果有,那么不需要登录,自动跳转首页; 如果没有, 那么就登录。
     function loadFn() {
        var token = localStorage.getItem('token')
        if (token) {
          //有
          location.href = './index.html'
        } else {
          alert('你需要登录')
        }
      }
    
    1. 解决跨域,在 login.js 里面设置请求头:
      在这里插入图片描述
    2. 前端发请求,在data里面加上token:
      在这里插入图片描述
    3. 产生公钥和私钥,在终端分别运行以下代码:
      在这里插入图片描述
    4. 后端判断 token 有没有值,安装 jsonwebtoken(npm i jsonwebtoken -S)
    //引入文件系统
    const fs = require('fs')
    const path = require('path')
    const jwt = require('jsonwebtoken')
    
    
       const { token, username, password } = req.body
        if (token) {
          //证明有值
          res.render('login', {
            data: JSON.stringify({
              info: '登录成功',
              status: 1
            })
          })
        } else {
          // 第一次登录 或是 token失效 
          // 重新生成token  返回给前台
          // 1. 通过文件系统读取私钥
          let private_key = fs.readFileSync(path.join(__dirname, '../rsa/private_key.pem'))
          var use_token = jwt.sign(username, private_key, { algorithm: 'RS256' });
          /* 
            payload: 负载也就是数据,这里是值用户名
            private_key: 通过openSSL 生成的私钥 
            RS256: 算法
          */
          res.render('login', {
            data: JSON.stringify({
              info: '登录成功',
              status: 1,
              token: use_token
            })
          })
        }
    
    1. 存 token:
    success(res) {
            const result = JSON.parse(res)
            if (result.status === 1) {
              localStorage.setItem('token', result.token)
            }
          }
    
    

    测试!

    展开全文
  • 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,然后两个比对一下,一致就验证通过。

    展开全文
  • Token

    千次阅读 2018-11-21 10:12:05
    1.Token是什么 Token是一个字符串,是一段根据特殊规则生成的唯一的、保存在缓存服务器(如Redis)中的key,这个key对应的value是用户账户数据。每个用户登录后,服务器生成一个类似Token并缓存,之后用户每次请求...

    1.Token是什么

    Token是一个字符串,是一段根据特殊规则生成的唯一的、保存在缓存服务器(如Redis)中的key,这个key对应的value是用户账户数据。每个用户登录后,服务器生成一个类似Token并缓存,之后用户每次请求中都要带上这个Token,以便实现类似于HTTP Session的会话跟踪。

    2.为什么要用Token

    随着近几年上网设备的多样化,软件项目的UI层不仅仅再是窗体、html、还有iOS、安卓app,因此基于Servlet的HTTP Session也就不再适用,因为可能没有浏览器和html页面,也没有cookie了。这种情况下、就需要Token技术来完成用户的跟踪,使得一个账户可以从html登录,也可以从手机app登其它客户端登录。

    3.Token如何生成

    每个HTTP数据包括两大主要部分,头部字段和消息体,Header是对一个HTTP数据包的描述信息,一般有多个字段和值。为了对不同客户端的Token区别对待,生成Token时需要判断客户端类型,拼接不同前缀。为了保证Token的唯一性和保密性,生成Token时还要拼接用户名+ID+日期时间+6位密码(盐,salt根据客户端信息生成);代码如下

        /**
         * 为了保证Token的唯一性和保密性,生成Token时还要拼接用户名(加密)+ID+日期时间+6位密码(盐,salt根据客户端信息生成);
         * @param agent
         * @param user
         * @return null
         */
        @Override
        public String generateToKen(String agent, ItripUser user) {
            try {//UserAgentUtil可检测客户端类型,因为要根据客户端的类型生成不同的token
                UserAgentInfo userAgentInfo = UserAgentUtil.getUasParser().parse(agent);
                StringBuilder sb = new StringBuilder();
                sb.append(tokenPrefix);
                if(userAgentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN)){//未知客户端类型
                    if(UserAgentUtil.CheckAgent(agent)){
                        sb.append("MOBILE-");//如果包含移动设备的关键字,拼接移动设备的Token前缀
                    }else {
                        sb.append("PC-");//拼接PC的Token前缀
                    }
                }else if(userAgentInfo.getDeviceType().equals("Personal computer")){
                    sb.append("PC-");//如果很明显是PC类型,拼接PC的TOken的前缀
                }else{
                    sb.append("MOBILE-");//拼接移动设备的Token前缀
                }
                sb.append(MD5.getMd5(user.getUserCode(),32) + "-");//拼接加密用户名称
                sb.append(user.getId() + "-");//拼接user id及日期
                sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "-");
                sb.append(MD5.getMd5(agent,6));//在拼接6个字符的密码
                return sb.toString();
            }catch (IOException e){
                e.printStackTrace();
            }
            return null;
        }

    4.Token登录与退出

    登录业务方法

        /**
         * 登录
         * @return 登录成功的user对象,失败返回null
         * @throws Exception
         */
        @Override
        public ItripUser login(String name, String password) throws Exception {
            ItripUser user = this.findByUsername(name);
            if(null != user&&user.getUserPassword().equals(password)){
                if(user.getActivated() != 1){
                    throw new UserLoginFailedException("用户未激活");
                }
                return user;
            }
            return null;
        }

    保存Token到Redis缓存

        /**
         * TokenServiceImpl中,保存Token到Redis缓存服务器
         * @param token
         * @param user
         */
        @Override
        public void save(String token, ItripUser user) {
            if(token.startsWith(tokenPrefix+"PC-")){
                //如果客户端是PC,Token有过期时间
                redisAPI.set(token,expire, JSON.toJSONString(user));
            }else {
                //客户端是手机,Token永不过期
                redisAPI.set(token,JSON.toJSONString(user));
            }
        }

    登录方法接收前端请求

      /**
         * 登陆方法接收前端请求
         * @param name
         * @param password
         * @param request
         * @return
         */
        @RequestMapping(value = "/dologin",method = RequestMethod.POST,produces = "application/json")
        @ResponseBody
        public Dto dologin(@RequestParam String name, @RequestParam String password,
                           HttpServletRequest request){
            if(!EmptyUtils.isEmpty(name) && !EmptyUtils.isEmpty(password)){
                ItripUser user = null;
                try {   //调用service进行登录
                    user = userService.login(name.trim(),MD5.getMd5(password.trim(),32));
                }catch (UserLoginFailedException e){//返回登录失败错误
                    return DtoUtil.returnFail(e.getMessage(),ErrorCode.AUTH_AUTHENTICATION_FAILED);
                }catch (Exception e){
                    e.printStackTrace();
                    return DtoUtil.returnFail(e.getMessage(),ErrorCode.AUTH_UNKNOWN);//返回未知错误
                }
                if(EmptyUtils.isNotEmpty(user)){//如果用户不为空,表示登录成功
                    String token = tokenService.generateToKen(
                            request.getHeader("user-agent"),user);//调用service生成Token
                    tokenService.save(token,user);//保存token到redis缓存
                    //计算token过期时间毫秒数,为系统当前事件毫秒数+7200s*1000毫秒/s,即2小时
                    long timeout = Calendar.getInstance().getTimeInMillis()+ToKenService.SESSION_TIMEOUT*1000;
                    //创建要返回的ItripTokenVO
                    ItripTokenVO tokenVO = new ItripTokenVO(token,timeout,Calendar.getInstance().getTimeInMillis());
                    //tokenVo转为JSON并响应给客户端
                    return DtoUtil.returnDataSuccess(tokenVO);
                }else{
                    return DtoUtil.returnFail("用户名密码错误",ErrorCode.AUTH_AUTHENTICATION_FAILED);
                }
            }else{
                return DtoUtil.returnFail("参数错误!检查提交的参数名是否正确。",ErrorCode.AUTH_PARAMETER_ERROR);
            }
        }

    验证token

        /**
         * 验证token是否有效:1.token在redis中是否存在;2.token是否过期
         */
        @Override
        public boolean validate(String agent, String token) {
            boolean tokenValid = false; //该变量表示token是否有效
            if(!redisAPI.exist(token)){ //如果token在redis不存在
                return tokenValid;      //返回false
            }
            try {
                String [] tokenDatails = token.split("-");//按-分割token字符串
                SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
                Date TokenGenTime = formatter.parse(tokenDatails[3]);//还原token生成时间
                long passed = Calendar.getInstance().getTimeInMillis() - TokenGenTime.getTime();//计算token已生成多久时间(毫秒数)
                if(passed > this.SESSION_TIMEOUT * 1000){//如果token已经过期
                    return tokenValid; //返回false
                }
                String agentMD5 = tokenDatails[4];
                if(MD5.getMd5(agent,6).equals(agentMD5)){   //验证token的6位密码                 
                    tokenValid = true;//如果token中的密码也一致,token有效
                }
            }catch (ParseException e){
                e.printStackTrace();
            }
            return tokenValid;
        }

    删除Token

        /**
         * 从redis删除token(key)和用户信息(value)
         * @param token
         */
        @Override
        public void delete(String token) {
            if(redisAPI.exist(token)){
                redisAPI.delete(token);
            }
        }

    退出登录方法

        @RequestMapping(value = "/logout",method = RequestMethod.GET,produces = "application/json",headers = "token")
        @ResponseBody
        public Dto logout(HttpServletRequest request){
            //从请求的Header中获取token
            String token = request.getHeader("token");
            //删除token和信息
            try {
                //验证Redis中是否有该token(且没有过期)
                if(!tokenService.validate(request.getHeader("user-agent"),token)) {
                    return DtoUtil.returnFail("token无效", ErrorCode.AUTH_TOKEN_INVALID);
                }else{
                    tokenService.delete(token);//从Redis删除
                    return DtoUtil.returnSuccess("注销成功!");
                }
            } catch (Exception e) {
                e.printStackTrace();
                return DtoUtil.returnFail("注销失败!", ErrorCode.AUTH_UNKNOWN);
            }
        }

     

    展开全文
  • 前后端分离下的token机制

    千次阅读 2019-06-01 19:38:51
    一、 什么是token ? Token 是在服务端产生的,如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。 二、token...

    一、 什么是 token ?

    Token 是在服务端产生的,如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。

    二、token 的适用场景

    登录:

     业务请求:

    Token 过期,刷新 Token:

     三、token的使用

    1.创建:token 的实质其实就是一个唯一标识的字符串,一般来说由UUID生成,或者一些复杂的可以由时间戳或者用户的ID根据某一算法进行加密生成,取到token之后再进行解密,取出用户的ID。我这里以简单的UUID进行举例:

    String token = UUID.randomUUID().toString();// 生成token

    2.保存:很多常见的做法是将token保存进数据库或者缓存,但是放进数据库会严重消耗服务器资源,所以本人不建议这样做,我比较喜欢把token 存进redis缓存里,最主要的好处就是简单易操作,还大大加快了数据查询速度,我把 redis 理解为一张庞大的Map 表。

    3.将 token 添加到响应头

    response.setHeader("Access-Control-Expose-Headers",
    				"Cache-Control,Content-Type,Expires,Pragma,Content-Language,Last-Modified,token");
    response.addHeader("token", token);

    4.从请求头里取出token

    response.getHeader("token");

     

    展开全文
  • syntax error near unexpected token `(' 脚本内容排查了很多遍都没有找到原因,执行的方式为 sh xx.sh 然后使用 source xx.sh 执行和 bash xx.sh执行都是 ok 的。 知识的了解 此时就需要了解 sh source bash 执行...
  • 在vue中如何获取token,并将token写进header

    万次阅读 多人点赞 2018-03-15 16:12:25
    在login.vue中通过发送http请求获取token//根据api接口获取token var url = this.HOST + "/session"; this.$axios.post(url, { username: this.loginForm.username, password: this.loginForm.pa...
  • Android studio 3.5 运行...10:12 Emulator: ERROR: Unable to access 'C:\Users\璋捣灞盶.emulator_console_auth_token': emulator console will not work 10:12 Gradle build finished in 10 s 159 ms 10:12 ...
  • 报错原因 内含中文字符串或者中文的符号;...我在做js使用DOM元素做拼接,报了这个错误,Uncaught SyntaxError: Invalid or unexpected token var posttitle="中国人"; $api.append(abcd, " <d...
  • 一,Android studio运行模拟器出现如下错误:Emulator: ERROR: Unable to access ‘C:\Users\鏉庨摥.emulator_console_auth_token’: emulator console will not work,在各种论坛找了大半天,终于找到了解决方法。...
  • 我需要通过公众号的access_token和openid去获取用户信息,由于微信限制请求tokentoken我是缓存起来定时再去请求的,然后在缓存时间内请求,微信返回{"errcode":40001,"errmsg":"invalid credential, access_token ...
  • 这是我的jwt 模块, ...我要在 app.js 中新增一个中间件拦截请求对token进行验证正确与否,是否过期,是否需要重新生成token,是否直接返回前端重新登录,这里边是怎样的一个过程以及实现token的刷新,失效处理的方法
  • 看了许多小伙伴分享的刷新token和判断token是否失效的方法,个人感觉有些难懂和不够简便。现结合个人开发实践分享一下使用vue axios请求拦截的方法来刷新token和判断token是否过期、失效的方法。 刷新tokentoken...
  • 即登陆时,要传一个登陆后的token才能访问的。 那这个怎么设置,才可以让所有接口都允许登陆后访问呢。 解决办法如下: @Configuration @EnableWebMvc @EnableSwagger2 public class SwaggerConfig { @Bean ...
  • node.js 实现 token 身份验证

    万次阅读 2020-01-20 14:29:48
    node + jwt 实现token身份验证 安装依赖 express-jwt npm i express-jwt 将token校验相关数据导入配置文件 // setting.js module.exports = { token: { // token密钥 signKey: 'blog_globM_token_key_$$$$'...
  • TOKEN 后端进行加密,前台接收后每次请求放在头部发给后台做解密效验 有以下疑惑: 1.TOKEN 放在头部的话 浏览器打开开发者工具是可以看到请求头上的TOKEN值的,很容易就获取到了。 2.被人拿到token值不就可以...
  • 生成token和解析token

    千次阅读 2019-07-11 10:03:03
    package com.zhanchuan.util; import com.auth0.jwt.JWTSigner; import com.auth0.jwt.JWTVerifier; import com.fasterxml.jackson.databind.ObjectMapper; import com.zhanchuan.logon.entity.TSmsCode;...import ...
  • oauth2 token刷新,token续期,access_token和refresh_token实效如何设置 token认证,生成的token 过一段时间就会失效(不要故意把时间设的很长,这样不安全,token变得毫无意义!),用户需要重新登录获取token。用户...
  • 把值保存到token里 ``` public String getToken(Long id ,String unionId) { String token=""; token= JWT.create().withAudience( String.valueOf(id ) )// 将 user id 保存到 token 里面 .sign( ...
  • 这是我目前的axios 拦截器,我怎么配置才能在刷新token之后重新发送之前的请求a,请求a指的是 发送a后检测到accesstoken已经过期导致失败的请求。 是需要每个请求单独配置,还是在拦截器里就能实现呢
  • 前言 在互联网的登陆系统中一般有session cookie 和 jwt token来进行验证用户是否登录的.下面来说一下关于 jwt的坑: 1.首先登录成功的返回,其中jwt的有效期为2小时,refreshJwt的有效期为30天如下: 2.保存jwt 和...
  • Syntax error on token “class”, invalid VariableDeclarator 标记“class”上的语法错误,variabledeclarator无效 想到class是Java中的关键字,我就把class改为了clas,结果发现问题解决了,这个问题告诉我们,...

空空如也

1 2 3 4 5 ... 20
收藏数 109,535
精华内容 43,814
关键字:

token