关于token:声网-Token-鉴权机制以及常见的问题

Token鉴权是什么?Token也称为动静密钥,是在退出频道时用于校验用户权限的一组字符串;鉴权是指在用户拜访你的零碎前,对其进行身份校验。用户在应用声网服务,如退出音视频通话或登录信令零碎时,声网会应用 Token 对其鉴权。 咱们为这种形式提供了一个较为形象的比喻,即: 某个展览馆须要游客实名认证后,获取专属入场券才可参观。游客在实现实名认证后能够获取到具备有效期限度的专属入场券,在进场时提供在有效期内的入场券,方能进场。其中: ● 展览馆相当于声网的服务,即音视频频道或信令零碎等;●专属入场券相当于 Token;●实名认证步骤相当于联合 声网 AppID、频道号、用户 ID 等信息 获取到专属 Token 的步骤;●进场时校验入场券相当于鉴权,即校验 Token 是否和 声网 AppID、频道号、用户 ID 等信息匹配,且在有效期内。 声网的产品和服务中大部分采纳Token 鉴权的形式。上面,咱们针对如何生成和应用 Token,以及 Token 鉴权中常见的问题进行具体的解说。 如何生成和应用Token?2.1 Token 鉴权原理在理解如何生成和应用Token 前,须要先理解 Token 鉴权的原理。 如图所示,共分为9个步骤: 1.客户端依据须要,向 app 服务端申请 Token2.App 服务端生成并返回 Token3.客户端以 UID、频道名以及获取到的 Token 退出频道4.声网平台读取该 Token 中蕴含的信息,并进行校验5.客户端收到退出频道胜利回调,并获取用户 UID6.Token 最大有效期为 24 小时。当即将过期时,客户端会收到 Token 行将过期的回调7.此时,如果客户端须要持续进行音视频互动,须要申请新的 Token8.App 服务端生成并返回 Token9.客户端更新 Token这个过程中,用户须要自行实现步骤1、2、3、7、8、9 的代码逻辑。 其中,对应的Token 蕴含以下信息: ● 你在声网控制台创立我的项目时生成的 App ID● 频道名● 用户 ID● 用户权限,如是否能发流或收流● Token 的过期工夫 2.2 申请与生成Token能够看到,在用户退出频道前,客户端须要先向服务器申请Token,并在 服务器 生成 Token,且 Token 必须与 须要退出频道的用户所对应的 AppID、频道名、用户 ID(UID)信息、用户权限(是否能发流或收流) 一一对应,并且确保生成的 Token 在有效期内。而后能力以 UID、频道号 和 Token 退出对应频道。 向服务器申请Token,能够通过向服务器发送 GET 申请等形式自行实现,以下文章以供参考: ● 部署 Token 服务器(官网文档):https://docs.agora.io/cn/live-streaming-premium-4.x/token_ser...●用Token-Flutter 连贯 Agora (2021-09-15) https://www.rtcdeveloper.cn/cn/community/blog/22929● 应用 Swift 部署声网 Token 服务器 (2022-10-27): https://www.rtcdeveloper.cn/cn/community/blog/24981●在NET Core 上建设 Agora AccessToken 服务 (2020-11-14):https://www.rtcdeveloper.cn/cn/community/blog/19790● 如何用 GoLang 为声网 Agora 利用构建 Token 服务器 (2020-12-09):https://www.rtcdeveloper.cn/cn/community/blog/20102● 如何应用 NodeJS 为声网 Agora 利用构建 Token 服务器 (2020-12-03): https://www.rtcdeveloper.cn/cn/community/blog/20024● 应用 Java 构建 Agora 令牌服务器 (2021-02-07):https://www.rtcdeveloper.cn/cn/community/blog/20709● 用 Java 构建声网令牌服务器 (2023-01-10): https://www.rtcdeveloper.cn/cn/community/blog/25430 ...

May 3, 2023 · 2 min · jiezi

关于token:JWT-Token在线编码生成

JWT Token在线编码生成JWT Token在线编码生成 JSON Web Token(缩写 JWT)是目前最风行的跨域认证解决方案。本工具提供在线编码的性能 JSON Web Token(缩写 JWT)是目前最风行的跨域认证解决方案。本工具提供在线编码的性能 https://tooltt.com/jwt-encode/

February 23, 2022 · 1 min · jiezi

关于token:用户登录设计之双token设计

背景笔者在做的一个我的项目之前的登录接口是实习生写的,登录设计就是简略的提交用户名明码获取token,而后token的过期工夫巨长是30天,于是乎另一位工作年限长一点的共事批改了代码,变成了登录获取两个token: 1.accessToken: 真正用来获取数据的权限2.refreshToken: 用来获取accessToken为什么须要双token?总所周知,token是为了避免用户信息传来传去导致被劫持,然而如果token没有过期工夫或者过期很长,那么显然token被劫持还是不平安的,token就失去了意义。 所以这时候大家必定都想:那么把token过期工夫设置的短一点就行啦? 是的,一般来讲accessToken的过期工夫应该要短一点,然而这时候对于用户来讲就麻烦了。 因为token过期就意味着要从新登录,设想下你正浏览的好好的,忽然让你掉线了并且要求你从新登录,心里必定是想骂人的。 什么时候须要用户从新登录? 次要有三种状况: 1.用户长时间无操作,也能够定义未不沉闷用户,就会被主动踢下,主动重定向到登录页面,超时工夫能够自定义设置;2.token生效,通常是双token都生效后,会要求从新登录获取新的双token;3.当检测到有危险的时候,能够要求从新登录,获取token;因而这时候就能够应用双token的设计,当两个token都过期了再要求用户从新登录,对于refreshToken,它只用来获取accessToken,不会频繁被用于申请,对于accessToken,它过期工夫十分短,即便被拦挡了解密也须要工夫,而token自身也很快过期,因而这样的设计更加平安。 那么就这样就大快人心了吗?显然不是作为前端,咱们还须要实现让用户对于应用refreshToken获取accessToken的操作是无感的,这样能力既保证更加平安正当,也不影响用户体验。 如何实现refreshToken获取token无感刷新?总的来说个别有三种办法: 1.通过后端返回过期工夫,前端依据以后工夫与这个过期工夫做判断,去调用刷新token接口毛病:须要后端额定提供一个Token过期工夫的字段;应用了本地工夫判断,若本地工夫被篡改,特地是本地工夫比服务器工夫慢时,拦挡会失败。 2.定时工作,定时应用refreshToken获取accessToken浪费资源,耗费性能,不倡议采纳。 3.做响应拦截器中拦挡,后端判断token 返回过期后,调用刷新token接口比拟好的计划,也是我在我的项目中应用的计划,并且axios有做响应拦挡的api 这里贴一个简略的实现demo: import axios from 'axios'; axios.interceptors.response.use(res => { // token异样 if (res.data.code === 409) { deleteToken(); router.push('/login') return Promise.reject(); // 更新Token } else if (res.data.code === 410) { const {token} = res.data setToken(token);// 重置token } return res && res.data})复制代码总结为了用户平安因而要应用token设计,为了解决用户频繁登录,采纳双token设计,同时前端也须要做肯定解决。 最初如果你感觉此文对你有一丁点帮忙,点个赞。或者能够退出我的开发交换群:1025263163互相学习,咱们会有业余的技术答疑解惑 如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点star: https://gitee.com/ZhongBangKe...不胜感激 !

December 1, 2021 · 1 min · jiezi

关于token:实现无感刷新token我是这样做的

前言 最近在做需要的时候,波及到登录token,产品提出一个问题:能不能让token过期工夫长一点,我频繁的要去登录。 前端:后端,你能不能把token 过期工夫设置的长一点。 后端:能够,然而那样做不平安,你能够用更好的办法。 前端:什么办法? 后端:给你刷新token的接口,定时去刷新token 前端:好,让我思考一下 需要 当token过期的时候,刷新token,前端须要做到无感刷新token,即刷token时要做到用户无感知,防止频繁登录。实现思路 办法一后端返回过期工夫,前端判断token过期工夫,去调用刷新token接口 毛病:须要后端额定提供一个token过期工夫的字段;应用了本地工夫判断,若本地工夫被篡改,特地是本地工夫比服务器工夫慢时,拦挡会失败。 办法二写个定时器,定时刷新token接口 毛病:浪费资源,耗费性能,不倡议采纳。 办法三在响应拦截器中拦挡,判断token 返回过期后,调用刷新token接口 实现 axios的根本骨架,利用service.interceptors.response进行拦挡 import axios from 'axios'service.interceptors.response.use( response => { if (response.data.code === 409) { return refreshToken({ refreshToken: localStorage.getItem('refreshToken'), token: getToken() }).then(res => { const { token } = res.data setToken(token) response.headers.Authorization = `${token}` }).catch(err => { removeToken() router.push('/login') return Promise.reject(err) }) } return response && response.data }, (error) => { Message.error(error.response.data.msg) return Promise.reject(error) })复制代码问题解决 ...

October 14, 2021 · 2 min · jiezi

关于token:基于-token-的多平台身份认证架构设计

我的公众号:MarkerHub,Java网站:https://markerhub.com更多精选文章请点击:Java笔记大全.md 小Hub领读:很多人都晓得token作为用户会话凭证,其实利用场景还有很多,分类也很多,文中论述了token的分类问题、隐衷性参数设置问题、应用场景问题、不同生命周期的 token 分层转化关系等;以及介绍了不同应用场景。 没想到,小小token竟然也有什么多知识点,涨见识了~ 作者:哈莫cnblogs.com/beer/p/6029861.html1、概述在存在账号体系的信息系统中,对身份的鉴定是十分重要的事件。 随着挪动互联网时代到来,客户端的类型越来越多, 逐步呈现了 一个服务器,N 个客户端的格局 。 不同的客户端产生了不同的用户应用场景,这些场景: 有不同的环境平安威逼不同的会话生存周期不同的用户权限管制体系不同级别的接口调用形式综上所述,它们的身份认证形式也存在肯定的区别。 本文将应用肯定的篇幅对这些场景进行一些剖析和梳理工作。 2、应用场景上面是一些在 IT 服务常见的一些应用场景: 用户在 web 浏览器端登录零碎, 应用零碎服务用户在手机端(Android/iOS)登录零碎, 应用零碎服务用户应用凋谢接口登录零碎, 调用零碎服务用户在 PC 解决登录状态时通过手机扫码受权手机登录(应用得比拟少)用户在手机解决登录状态进通过手机扫码受权 PC 进行登录(比拟常见)通过对场景的细分, 失去如下不同的认证 token 类别: 1、原始账号密码类别 用户名和明码API 利用 ID/KEY2、会话 ID 类别 浏览器端 token挪动端 tokenAPI 利用 token3、接口调用类别 接口拜访 token身份受权类别PC 和挪动端互相受权的 token3、token 的类别不同场景的 token 进行如下几个维度的比照: 人造属性比照:1、应用老本 本认证形式在应用的时候, 造成的不便性。比方: 账号密码须要用户关上页面而后一一键入二维码须要用户掏出手机进行扫码操作2、变动老本 本认证形式, token 发生变化时, 用户须要做出的相应更改的老本: 用户名和明码发生变化时, 用户须要额定记忆和从新键入新密码API 利用 ID/KEY 发生变化时, 第三方利用须要从新在代码中批改并部署受权二维码发生变化时, 须要用户从新关上手机利用进行扫码环境危险 被偷窥的危险被抓包的危险被伪造的危险可调控属性比照:1、应用频率 在网路中传送的频率 2、无效工夫 此 token 从创立到终结的生存工夫 ...

March 23, 2021 · 2 min · jiezi

关于token:axios封装结合token

1.开发环境 vue+axios+typescript2.电脑系统 windows10专业版3.在开发的过程中,咱们在做登录的时候会被要求应用token进行登录,上面我来分享一下应用办法(只是简略的封装),心愿对你有所帮忙。4.在src目录下新建4-1.chenhttp.js代码如下: import axios from 'axios'import qs from "qs"// 环境的切换if (process.env.NODE_ENV === 'development') { // axios.defaults.baseURL = '/api'; axios.defaults.baseURL = '/api';} else if (process.env.NODE_ENV === 'debug') { axios.defaults.baseURL = ''} else if (process.env.NODE_ENV === 'production') { axios.defaults.baseURL = ''}axios.defaults.timeout = 10000;axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8;multipart/form-data';// 增加申请拦截器,在申请头中加tokenaxios.interceptors.request.use( config => { if (localStorage.getItem('Cusertoken')) { config.headers.Ctoken = localStorage.getItem('Cusertoken'); } return config; }, error => { return Promise.reject(error); });// 封装axios的post申请const post = (url, params) => { return new Promise((resolve, reject) => { axios .post(url, qs.stringify(params)) .then(response => { resolve(response.data); }) .catch(error => { reject(error); }); });};// 封装axios的put申请const put = (url, params) => { return new Promise((resolve, reject) => { axios .put(url, params) .then(response => { resolve(response.data); }) .catch(error => { reject(error); }); });};// 封装axios的delete申请const del = (url, params) => { return new Promise((resolve, reject) => { axios .delete(url, params) .then(response => { resolve(response.data); }) .catch(error => { reject(error); }); });};const get = (url, query) => { return axios.get(url, query);};const $axios = { post: post, get: get, put: put, delete: del};export default $axios;4-2.chenapi.js对立接口治理,小伙伴们依据本人的需要进行批改,代码如下: ...

January 25, 2021 · 2 min · jiezi

关于token:JWT工具类

工具开源地址 欢送大家搜寻“小猴子的技术笔记”关注我的公众号,有问题能够及时和我交换。 之前咱们曾经理解到了什么是JWT以及JWT的长处,那么怎么在我的项目中应用到JWT呢?首先咱们须要在maven的我的项目中引入JWT的依赖: <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.12.0</version></dependency> 胜利引入jar包之后就能够进行token的生成了。之后须要指定一个加密的算法,也就是须要你本人提供一个秘钥串来进行加密。你能够把它了解为之前做MD5加密的时候加上的盐值。 Algorithm algorithm = Algorithm.HMAC256("this is your secret") 咱们有十种算法能够抉择: 而后咱们就能够利用jar包中的办法,创立一个token并且附带上签名,之后就可能失去一个残缺的token令牌。 public static void main(String[] args) { Algorithm algorithm = Algorithm.HMAC256("this is your secret"); String token = JWT.create().sign(algorithm); System.out.println(token);}eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.e30.svmNHFYzrAj6USTjLekz3CTFyEdmpRkE8A8x3CbPe1Q 这仅仅是一个简略的token的生成,有的时候咱们还想在token中传递咱们的信息,就能够应用上面这个办法: public static void main(String[] args) { Algorithm algorithm = Algorithm.HMAC256("this is your secret"); String token = JWT.create().withClaim("name", "小猴子").sign(algorithm); System.out.println(token);} 如果你有很多条件须要传递就能够“withClaim()”多个值。须要留神的是:这里请不要传递敏感的信息免得token被破解,信息泄露。 咱们在开发中一个token必定不可能始终应用,肯定有个过期工夫。那么在JWT中怎么定义token的过期工夫呢?JWT内置了为咱们设置token的过期工夫策略,官网给提供了一个“withExpireAt()”的办法。须要传递一个日期参数来指定过期工夫。 public static void main(String[] args) { Algorithm algorithm = Algorithm.HMAC256("this is your secret"); JWTCreator.Builder builder = JWT.create().withClaim("name", "小猴子"); String token = builder.withExpiresAt(new Date(System.currentTimeMillis() + 2000)).sign(algorithm); System.out.println(token);} 生成token: ...

January 18, 2021 · 2 min · jiezi

关于token:ECDSA密钥对生成以及在Token中的应用

1 概述本文次要讲述了如何利用Openssl生成ECDSA密钥对,并利用Auth0库进行Token生成及验证的过程。 2 ECDSA2.1 简介ECC(Elliptic Curve Cryptography,椭圆曲线加密)是一种基于椭圆曲线数学的公钥加密算法,而ECDSA是应用ECC对数字签名算法(DSA)的模仿,总的来说ECC相比起常见的RSA更加平安并且生成密钥对的过程会更快。本文不会波及过多原理性的货色,只是作简略的介绍,想要详情理解这些算法的能够戳这里。 2.2 密钥对生成在Openssl中生成ECDSA密钥对的流程如下: openssl ecparam -genkey -name secp521r1 -out private.pem #生成私钥openssl ec -in private.pem -pubout -out public.pem #生成公钥参数阐明如下: ecparam:EC参数设置以及生成命令-genkey:应用特定参数生成EC私钥-name:ec参数,能够应用openssl ecparam -list_curves 查看,这里用的是secp521r1-out:输入文件名ec:EC密钥解决命令-in:输出文件-pubout:默认状况下会输入私钥,加上该选项会变成输入公钥(如果输出是公钥的状况下该参数会主动设置)执行完命令后就胜利生成密钥对了,能够查看一下: 密钥对生成之后就能够筹备一下生成Token了。 3 Auth0中的Token利用3.1 Auth0Auth0提供了验证以及受权服务,这里利用官网提供的Java实现去生成Token(这里插一句题外话,Java罕用的Token实现还有一个叫JJWT的库),首先引入包: <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.12.0</version></dependency>Gradle: compile group: 'com.auth0', name: 'java-jwt', version: '3.12.0'引入起初看一下反对的加密算法,如下图: 最简略的应用HMAC算法生成的Token如下: System.out.println(JWT.create().withIssuer("issuer").withAudience("content").sign(Algorithm.HMAC512("password")));当然这不是本文的重点,本文的重点是介绍如何利用ECDSA去生成Token。 首先Auth0提供的签名api如下: JWT.create().sign(Algorithm)其中Algorithm能够取值如下: 想要应用ECDSA算法须要提供一个ECDSAKeyProvider或一个ECPublicKey和一个ECPrivateKey,这里抉择后一种形式实现。 3.2 密钥对解决官网并没有提供如何生成ECPublicKey/ECPrivateKey的办法,甚至连从文件读取密钥对的办法都没有提供,笔者从官网提供的测试代码中发现了如下办法: 其中外围就是读取密钥对的两个办法: readPublicKeyFromFilereadPrivateKeyFromFile从import后果能够看到这是一个工具类: 但问题是官网该工具类是测试应用的,换句话说不对外裸露的,在IDEA中间接引入会报错: 因而间接找到该工具类的源码(链接能够戳这里,须要引入bouncycastle包,Maven仓库链接能够戳这里) package com.auth0.jwt;import org.bouncycastle.util.io.pem.PemObject;import org.bouncycastle.util.io.pem.PemReader;import java.io.File;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;import java.security.KeyFactory;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.PublicKey;import java.security.spec.EncodedKeySpec;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;public class PemUtils { private static byte[] parsePEMFile(File pemFile) throws IOException { if (!pemFile.isFile() || !pemFile.exists()) { throw new FileNotFoundException(String.format("The file '%s' doesn't exist.", pemFile.getAbsolutePath())); } PemReader reader = new PemReader(new FileReader(pemFile)); PemObject pemObject = reader.readPemObject(); byte[] content = pemObject.getContent(); reader.close(); return content; } private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) { PublicKey publicKey = null; try { KeyFactory kf = KeyFactory.getInstance(algorithm); EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); publicKey = kf.generatePublic(keySpec); } catch (NoSuchAlgorithmException e) { System.out.println("Could not reconstruct the public key, the given algorithm could not be found."); } catch (InvalidKeySpecException e) { System.out.println("Could not reconstruct the public key"); } return publicKey; } private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) { PrivateKey privateKey = null; try { KeyFactory kf = KeyFactory.getInstance(algorithm); EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); privateKey = kf.generatePrivate(keySpec); } catch (NoSuchAlgorithmException e) { System.out.println("Could not reconstruct the private key, the given algorithm could not be found."); } catch (InvalidKeySpecException e) { System.out.println("Could not reconstruct the private key"); } return privateKey; } public static PublicKey readPublicKeyFromFile(String filepath, String algorithm) throws IOException { byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); return PemUtils.getPublicKey(bytes, algorithm); } public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException { byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); return PemUtils.getPrivateKey(bytes, algorithm); }}间接复制该工具类后,将前一步生成的private.pem以及public.pem搁置适合地位,通过工具类读取并生成Token: ...

December 31, 2020 · 2 min · jiezi

关于token:什么是token

对于初学者来说,对Token和Session的应用难免会限于窘境,开发过程中晓得有这个货色,但却不晓得为什么要用他?更不晓得其原理,明天我就带大家一起剖析剖析这货色。     一、咱们先解释一下他的含意: 1、Token的引入:Token是在客户端频繁向服务端申请数据,服务端频繁的去数据库查问用户名和明码并进行比照,判断用户名和明码正确与否,并作出相应提醒,在这样的背景下,Token便应运而生。 2、Token的定义:Token是服务端生成的一串字符串,以作客户端进行申请的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,当前客户端只需带上这个Token前来申请数据即可,无需再次带上用户名和明码。 3、应用Token的目标:Token的目标是为了加重服务器的压力,缩小频繁的查询数据库,使服务器更加强壮。     理解了Token的意义后,咱们就更明确的晓得为什么要用他了。     二、如何应用Token?     这是本文的重点,在这里我就介绍罕用的两种形式。 1、用设施号/设施mac地址作为Token(举荐)     客户端:客户端在登录的时候获取设施的设施号/mac地址,并将其作为参数传递到服务端。     服务端:服务端接管到该参数后,便用一个变量来接管同时将其作为Token保留在数据库,并将该Token设置到session中,客户端每次申请的时候都要对立拦挡,并将客户端传递的token和服务器端session中的token进行比照,如果雷同则放行,不同则回绝。 剖析:此刻客户端和服务器端就对立了一个惟一的标识Token,而且保障了每一个设施领有了一个惟一的会话。该办法的毛病是客户端须要带设施号/mac地址作为参数传递,而且服务器端还须要保留;长处是客户端不需从新登录,只有登录一次当前始终能够应用,至于超时的问题是有服务器这边来解决,如何解决?若服务器的Token超时后,服务器只需将客户端传递的Token向数据库中查问,同时并赋值给变量Token,如此,Token的超时又从新计时。 2、用session值作为Token     客户端:客户端只需携带用户名和明码登陆即可。     客户端:客户端接管到用户名和明码后并判断,如果正确了就将本地获取sessionID作为Token返回给客户端,客户端当前只需带上申请数据即可。 剖析:这种形式应用的益处是不便,不必存储数据,然而毛病就是当session过期后,客户端必须从新登录能力进行拜访数据。     三、应用过程中呈现的问题以及解决方案? 方才咱们轻松介绍了Token的两种应用形式,然而在应用过程中咱们还呈现各种问题,Token第一种办法中咱们暗藏了一个在网络不好或者并发申请时会导致多次重复提交数据的问题。     该问题的解决方案:将session和Token套用,如此便可解决,如何套用呢?请看这段解释:      这就是解决反复提交的计划。      总结:以上是集体对开发中应用Token和session的一点总结,如有叙述不当之处请斧正,我将及时改过并感激,我晓得还有更多更好的应用形式,我在这里只是抛砖引玉,心愿大家将您的应用形式提出来,咱们一起探讨,学习,一起提高,同时也为像我一样对这方面了解单薄的敌人提供点帮忙,谢谢 作者:9264oo链接:https://www.jianshu.com/p/248...起源:简书著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。

November 17, 2020 · 1 min · jiezi

关于token:如何获取变量token的值

一.什么是token 客户端应用用户名跟明码申请登录服务端收到申请,去验证用户名与明码验证胜利后,服务端会签发一个 Token,再把这个 Token 发送给客户端客户端收到 Token 当前能够把它存储起来,比方放在 Cookie 里或者 LocalStorage 里,客户端每次向服务端申请资源的时候须要带着服务端签发的 Token服务端收到申请,而后去验证客户端申请外面带着的 Token,如果验证胜利,就向客户端返回申请的数据web/APP登录的时候发送加密的用户名和明码到服务器,服务器验证用户名和明码,如果胜利,以某种形式比方随机生成32位的字符串作为token,存储到服务器中,并返回 token 到web/APP,当前web/APP申请时但凡须要验证的中央都要带上该 token,而后服务器端验证 token,胜利返回所须要的后果,失败返回错误信息,让他从新登录。其中服务器上 token 设置一个有效期,每次web/APP申请的时候都验证token 和有效期。二、如何获取token的值,进行接口测试。 接口测试的工具大部分都能够获取登录之后返回的token值,这里给大家解说如何用apipost获取token值的办法。 先关上apipost,进行登录接口的编写,而后获取token的值。 接着咱们来援用这个token的值,援用token的值须要咱们先设置环境变量 环境抉择为新建好的环境,在援用url地址。援用格局为{{变量名}} 在去设置后执行脚本获取token值,“token”是参数名称,response.json.token的意思是返回的json数据中的token值。 这些都设置好之后,就能够援用token了,token援用的办法和环境变量设置的url援用办法一样也是{{token}} 三、接口流程测试。 token值援用好之后,就能够进行接口流程化测试了。 抉择接口点击增加到流程测试中 进行流程测试 这就是如何获取token值进行接口流程测试的步骤了。 apipost工具下载地址: ApiPost - 可间接生成文档的API调试、管理工具www.apipost.cn

October 29, 2020 · 1 min · jiezi

springboot整合token

写在前面在前后端交互过程中,为了保证信息安全,我们往往需要加点用户验证。本文介绍了用springboot简单整合token。springboot版本2.2.0。另外主要用到了jjwt,redis。阅读本文,你大概需要花费7-10分钟时间整合token1. 导入相关依赖pom.xml文件中 <!-- jwt 加密解密工具类--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>2.TokenUtil.java实现生成/解析tokenpackage com.dbc.usermanager.util;import com.dbc.usermanager.service.RedisService;import io.jsonwebtoken.Claims;import io.jsonwebtoken.JwtBuilder;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.springframework.beans.factory.annotation.Autowired;import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;import java.security.Key;import java.util.Date;public class TokenUtil { /** * 签名秘钥,可以换成 秘钥 注入 */ public static final String SECRET = "DaTiBao";//注意:本参数需要长一点,不然后面剪切的时候很可能长度为0,就会报错 /** * 签发地 */ public static final String issuer = "dtb.com"; /** * 过期时间 */ public static final long ttlMillis = 3600*1000*60; /** * 生成token * * @param id 一般传入userName * @return */ public static String createJwtToken(String id,String subject) { return createJwtToken(id, issuer, subject, ttlMillis); } public static String createJwtToken(String id) { return createJwtToken(id, issuer, "", ttlMillis); } /** * 生成Token * * @param id 编号 * @param issuer 该JWT的签发者,是否使用是可选的 * @param subject 该JWT所面向的用户,是否使用是可选的; * @param ttlMillis 签发时间 (有效时间,过期会报错) * @return token String */ public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) { // 签名算法 ,将对token进行签名 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 生成签发时间 long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // 通过秘钥签名JWT byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET); String str=signatureAlgorithm.getJcaName(); Key signingKey = new SecretKeySpec(apiKeySecretBytes, str); // 让我们设置JWT声明 JwtBuilder builder = Jwts.builder().setId(id) .setIssuedAt(now) .setSubject(subject) .setIssuer(issuer) .signWith(signatureAlgorithm, signingKey); // if it has been specified, let's add the expiration if (ttlMillis >= 0) { //过期时间 long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp); } // 构建JWT并将其序列化为一个紧凑的url安全字符串 return builder.compact(); } /** * Token解析方法 * @param jwt Token * @return */ public static Claims parseJWT(String jwt) { // 如果这行代码不是签名的JWS(如预期),那么它将抛出异常 Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET)) .parseClaimsJws(jwt).getBody(); return claims; } public static void main(String[] args) { String token = TokenUtil.createJwtToken("2","ltz"); System.out.println(TokenUtil.createJwtToken("2","ltz")); Claims claims = TokenUtil.parseJWT(token); System.out.println(claims); }}3.新增登录验证的注解@LoginRequiredpackage com.dbc.usermanager.util;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;//加入此注解,就需要token@Target({ElementType.METHOD, ElementType.TYPE})// 表明此注解可用在方法名上@Retention(RetentionPolicy.RUNTIME)// 运行时有效public @interface LoginRequired { boolean required() default true;}4.测试 @PostMapping(value = "test") @ApiOperation(value="生成token") public ResultJson test(@RequestBody JSONObject requestJson){ String token= TokenUtil.createJwtToken("1","dtb"); redisService.set(token,"1"); return new ResultJson(0,"测试成功",null); } @GetMapping(value = "getToken") @LoginRequired @ApiOperation("") public ResultJson getToken(String token){ if(redisService.exists(token)){ System.out.println(redisService.get(token)); } return new ResultJson(0,"测试成功",null); }最后实体类User.java等相关文件就不贴出来了,大家可以用自己写好的实体类去编写。很多步骤与思想都在代码中体现,代码中也加了很多注释,你可以根据自己的需求进行增删查改。

November 5, 2019 · 2 min · jiezi

jwt

const jwt = require('jsonwebtoken')// 定义签名const key = 'token'const Token = { encrypt: function (data, time = 60) { // string加密数据; time过期时间,默认3天后过期,单位秒 return jwt.sign(data.toJSON(), key, {expiresIn: time}) }, decrypt: function (token) { try { let data = jwt.verify(token, key) console.log('data', data) return { token: true, data } } catch (e) { return { token: false, data: e } } }}module.exports = Token

May 21, 2019 · 1 min · jiezi

大话javascript 7期:Cookie、Session和Token的那些事儿

一、登录认证机制随着互联网的不断发展,无论是网站还是app,一般都会要求用户注册/登录。主要的登录方式有账户密码登录、第三方登录(微信登录、QQ登录、微博登录等)登录可分为三个阶段(登录验证、登录持续、退出登录);登录验证指客户端提供账号/密码(或第三方平台(微信、qq)获取openid/unionid)向服务器提出登录请求,服务器应答请求判断能否登录并返回相应数据;登录持续指客户端登录后, 服务器能够分辨出已登录的客户端,并为其持续提供登录权限的服务器。退出登录指客户端退出登录状态。二、保持登录持续状态的实现方式为什么要保持登录状态的持续?由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证方案:客户端登录成功后, 服务器为其分配一个唯一的凭证, 客户端每次请求资源时都带上这个凭证;实现方案cookie 会话机制session 会话机制token 会话机制三、Cookie、Session和TokenCookie(浏览器缓存)1.什么是CookieCookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。cookie其实是补充http协议的无状态性的缺点,底层是通过服务器端在http响应消息中增加set-cookie字段来将cookie信息发送给浏览器端,因为它只能存4k,一般用来存浏览器的身份信息,浏览器在访问服务器的某些资源的时候,会在http请求头中将cookie数据传给服务器,这样服务器就知道是谁请求的了,但是如果用户清除了cookie,那就啥都没有了2.Cookie的属性1、Expires:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效(持久级别Cookie)。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效(会话级别Cookie),浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1;2、Domain: 我们现在有二个域名。域名A:b.f.com,域名B:d.f.com;显然域名A和域名B都是f.com的子域名如果我们在域名A中的Cookie的domain设置为.f.com,那么.f.com及其子域名都可以获取这个Cookie,即域名A和域名B都可以获取这个Cookie如果域名A没有显式设置Cookie的domain方法,那么domain就为.b.f.com,不一样的是,这时,域名A的子域名将无法获取这个CookieHttpOnly: 这个属性是面试的时候常考的,如果这个属性设置为true,就不能通过js脚本来获取cookie的值,能有效的防止xss攻击3.Cookie的操作封装cookie的常用操作方法设置cookie读取cookie删除cookievar cookieUtil = { getItem: function (name) { var cookieName = encodeURIComponent(name) + “=”, cookieStart = document.cookie.indexOf(cookieName), cookieValue = null; if (cookieStart > -1) { var cookieEnd = document.cookie.indexOf(’;’, cookieStart); if (cookieEnd == 1) { cookieEnd = document.cookie.length; } cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd)) } return cookieValue; }, setItem: function (name, value, expires, path, domain, secure) { var cookieText = encodeURIComponent(name) + “=” + encodeURIComponent(value); if (expires) { cookieText += “;expires=” + expires.toGMTString(); } if (path) { cookieText += “;path=” + path; } if (domain) { cookieText += “;domain=” + domain; } if (secure) { cookieText += “;secure”; } document.cookie = cookieText; }, unset: function (name, path, domain, secure) { this.setItem(name, “”, new Date(0), path, domain, secure) }}CookieUtil.setItem(“name”, ’tom’); // 设置cookieconsole.log(CookieUtil.getItem(’name’));//读取cookieCookieUtil.unset(“name”)//删除cookie4.Cookie防篡改机制因为Cookie是存储在客户端,用户可以随意修改。所以,存在一定的安全隐患。防篡改签名:服务器为每个Cookie项生成签名。如果用户篡改Cookie,则与签名无法对应上。以此,来判断数据是否被篡改。原理如下:服务端提供一个签名生成算法secret根据方法生成签名secret(wall)=34Yult8i将生成的签名放入对应的Cookie项username=wall|34Yult8i。其中,内容和签名用|隔开。服务端根据接收到的内容和签名,校验内容是否被篡改。举个栗子:比如服务器接收到请求中的Cookie项username=pony|34Yult8i,然后使用签名生成算法secret(pony)=666。 算法得到的签名666和请求中数据的签名不一致,则证明数据被篡改。Session(会话)1.什么是sessionsession是一种服务器机制,是存储在服务器上的信息,主要配合cookie完成浏览器的身份认证和状态存储方式多种多样,可以是服务器的内存中,或者是mongo数据库,redis内存数据库中。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。Session相对于cookie较安全点,当用户请求服务器的时候,服务器会把数据临时存下来,如果退出网站后,session会被销毁。Session是基于cookie实现的,浏览器第一次访问服务器时,服务器创建一个Session,同时生成一个唯一的会话key,即sessionID。接着sessionID及session分别作为key和value保存到缓存中,也可以保存到数据库中,然后服务器把sessionID通过set-cookie的方式写入浏览器,浏览器下次访问服务器时直接携带上cookie中的sessionID,服务器再根据sessionID找到对应的session进行匹配,来判断用户是否登录2.session鉴权过程【1】 客户端发起登录请求,服务器端创建session,并通过set-cookie将生成的sessionID写入的客户端的cookie中。【2】 在发起其他需要权限的接口的时候,客户端的请求体的Header部分会携带sessionID发送给服务端。然后根据这个sessionId去找服务器端保存的该客户端的session,然后判断该请求是否合法。3.cookie和session的区别Token(身份令牌)1.什么是tokentoken的意思是“令牌”,是用户身份的验证方式,最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,由token的前几位+盐以哈希算法压缩成一定长的十六进制字符串,可以防止恶意第三方拼接token请求服务器)。还可以把不变的参数也放进token,避免多次查库浏览器第一次访问服务器,根据传过来的唯一标识userId,服务端会通过一些算法,如常用的HMAC-SHA256算法,然后加一个密钥,生成一个token,然后通过BASE64编码一下之后将这个token发送给客户端;客户端将token保存起来,下次请求时,带着token,服务器收到请求后,然后会用相同的算法和密钥去验证token,如果通过,执行业务操作,不通过,返回不通过信息;2.token生成方式浏览器第一次访问服务器时,服务器根据传过来的唯一标识userId,通过一些算法,加一个密钥,生成一个token,接着通过base64编码将token返回给客户端。客户端将token保存起来,下次请求时需要带着token,服务器收到请求后,用相同的算法和密钥去验证token3.token和session的区别token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;session服务器会保存一份,可能保存到缓存,文件,数据库;同样,session和token都是有过期时间一说,都需要去管理过期时间;其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。虽然确实都是“客户端记录,每次访问携带”,但 token 很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证,每次当场得出合法 /非法的结论。这一切判断依据,除了固化在 CS 两端的一些逻辑之外,整个信息是自包含的。这才是真正的无状态。 而 sessionid ,一般都是一段随机字符串,需要到后端去检索 id 的有效性。万一服务器重启导致内存里的 session 没了呢?万一 redis 服务器挂了呢?sessionID是基于cookie实现的,而token不需要基于cookie。这就导致了sessionID只能用在浏览器上,对于原生的应用无法实现。原生的应用是不具备cookie的特性的。另外sessionID可以实现服务端注销会话,而token不能(当然你可以把用户登陆的token存入到redis中,但是不推荐token入库)4.token的优点Token作为用户认证的处理方式,有几个优点:无状态,可扩展:不会在服务端存储用户的登录状态,可以很容易的实现服务器的增减支持移动设备,对多类型客户端的支持良好支持跨程序调用,各个接口之间的调用更方便安全可靠5.什么是JSON Web TokenJSON web Token,简称JWT,本质是一个token,是一种紧凑的URL安全方法,用于在网络通信的双方之间传递。一般放在HTTP的headers参数里面的authorization里面,值的前面加Bearer关键字和空格。除此之外,也可以在url和request body中传递。Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。如果你觉得这篇文章对你有所帮助,那就顺便点个赞吧,点点关注不迷路~黑芝麻哇,白芝麻发,黑芝麻白芝麻哇发哈!前端哇发哈 ...

April 17, 2019 · 1 min · jiezi

SpringBoot JWT Token 跨域 Preflight response is not successful

一、Springboot实现token校验SpringBoot实现token校验,可以通过Filter或者HandlerInterceptor,两种方式都可以,Filter在最外层,请求首先会通过Filter,filter允许请求才会通过Intercept。下面以HandlerInterceptor实现为例1.实现HandlerInterceptor,拦截请求校验tokenpublic class AuthenticationInterceptor implements HandlerInterceptor { private static final String URI_PASS_TOKEN = “/user/login”; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { log.info(“authentication interceptor preHandle path:{} uri:{}",httpServletRequest.getServletPath(),httpServletRequest.getRequestURI());// if (“OPTIONS”.equalsIgnoreCase(httpServletRequest.getMethod())) {// return true;// } if (httpServletRequest.getRequestURI().endsWith(URI_PASS_TOKEN)) { return true; } //从http header里面获取token String token = httpServletRequest.getHeader(“token”); if (StringUtils.isEmpty(token)) { throw new AuthenticationException(CODE_AUTHENTICATION_FAILED,“token is empty”); } Algorithm algorithm = Algorithm.HMAC256(JwtConstant.TOKEN_CREATE_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); try { verifier.verify(token); }catch (Exception ex){ throw new AuthenticationException(CODE_AUTHENTICATION_FAILED,ex.getMessage()); } return true; } @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 { }}2.Configuration配置,实现自动注入@Configurationpublic class InterceptorConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()) .addPathPatterns(”/**"); } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); }}二、前端调用 跨域 Preflight response is not successful通过单元测试、PostMan测试都可以调同,但是vue前端怎么都无法调用,错误如下:参考https://segmentfault.com/a/11…发现是浏览器发出的OPTIONS预检请求被HandlerInterceptor拦截了,因此在HandlerInterceptor添加如下代码: if (“OPTIONS”.equalsIgnoreCase(httpServletRequest.getMethod())) { return true; }对于options的请求不进行token检测即可 ...

April 16, 2019 · 1 min · jiezi

Swoft 系列教程:(2)认证服务及组件

Swoft 提供了一整套认证服务组件,基本做到了配置后开箱即用。用户只需根据自身业务实现相应的登录认证逻辑,框架认证组件会调用你的登录业务进行token的签发,而后的请求中token解析、合法性验证也都由框架提供,同时框架开放了token权限认证接口给用户,我们需根据自身业务实现token对当前访问资源权限的认证。下面我们详细讲一下 jwt 的签发及验证、访问控制的流程。token 签发token 签发的基本流程为请求用户登录认证服务,认证通过则签发token。Swoft 的认证组件为我们完成了token签发工作,同时 Swoft 约定了一个Swoft\Auth\Mapping\AuthManagerInterface::login方法作为用户的认证业务的入口。使用到的组件及服务:#认证组件服务,使用此接口类名作为服务名注册到框架服务中Swoft\Auth\Mapping\AuthManagerInterface::class#框架认证组件的具体实现者 token 的签发、合法校验、解析Swoft\Auth\AuthManager#token的会话载体 存储着token的信息Swoft\Auth\Bean\AuthSession#约定用户的认证业务需实现返回Swoft\Auth\Bean\AuthResult的login方法和bool的authenticate的方法Swoft\Auth\Mapping\AccountTypeInterface#用于签发token的必要数据载体 iss/sub/iat/exp/data 传递给 Swoft\Auth\AuthManager 签发 tokenSwoft\Auth\Bean\AuthResult配置项:config/properties/app.php设定auth模式jwtreturn [ … ‘auth’ => [ ‘jwt’ => [ ‘algorithm’ => ‘HS256’, ‘secret’ => ‘big_cat’ ], ] …];config/beans/base.php为\Swoft\Auth\Mapping\AuthManagerInterface::class服务绑定具体的服务提供者return [ ‘serverDispatcher’ => [ ‘middlewares’ => [ … ], … ], // token签发及合法性验证服务 \Swoft\Auth\Mapping\AuthManagerInterface::class => [ ‘class’ => \App\Services\AuthManagerService::class ],];App\Models\Logic\AuthLogic实现用户业务的认证,以 Swoft\Auth\Mapping\AccountTypeInterface 接口的约定实现了 login/authenticate方法。login方法返回Swoft\Auth\Bean\AuthResult对象,存储用于jwt签发的凭证:setIdentity 对应 sub,即jwt的签发对象,一般使用uid即可setExtendedData 对应 payload, 即jwt的载荷,存储一些非敏感信息即可authenticate方法签发时用不到,主要在验证请求的token合法性时用到,即检测jwt的sub是否为本平台合法用户<?phpnamespace App\Models\Logic;use Swoft\Auth\Bean\AuthResult;use Swoft\Auth\Mapping\AccountTypeInterface;class AuthLogic implements AccountTypeInterface{ /** * 用户登录认证 需返回 AuthResult 对象 * 返回 Swoft\Auth\Bean\AuthResult 对象 * @override Swoft\Auth\Mapping\AccountTypeInterface * @param array $data * @return AuthResult / public function login(array $data): AuthResult { $account = $data[‘account’]; $password = $data[‘password’]; $user = $this->userDao->getByConditions([‘account’ => $account]); $authResult = new AuthResult(); // 用户验证成功则签发token if ($user instanceof User && $this->userDao->verifyPassword($user, $password)) { // authResult 主标识 对应 jwt 中的 sub 字段 $authResult->setIdentity($user->getId()); // authResult 附加数据 jwt 的 payload $authResult->setExtendedData([self::ID => $user->getId()]); } return $authResult; } /* * 验证签发对象是否合法 这里我们简单验证签发对象是否为本平台用户 * $identity 即 jwt 的 sub 字段 * @override Swoft\Auth\Mapping\AccountTypeInterface * @param string $identity token sub 字段 * @return bool / public function authenticate(string $identity): bool { return $this->userDao->exists($identity); }}Swoft\Auth\AuthManager::login 要求传入用户业务的认证类,及相应的认证字段,根据返回Swoft\Auth\Bean\AuthResult对象判断登录认证是否成功,成功则签发token,返回Swoft\Auth\Bean\AuthSession对象。App\Services\AuthManagerService用户认证管理服务,继承框架Swoft\Auth\AuthManager做定制扩展。比如我们这里实现一个auth方法供登录请求调用,auth 方法中则传递用户业务认证模块来验证和签发token,获取token会话数据。<?php/* * 用户认证服务 * User: big_cat * Date: 2018/12/17 0017 * Time: 16:36 /namespace App\Services;use App\Models\Logic\AuthLogic;use Swoft\Redis\Redis;use Swoft\Bean\Annotation\Bean;use Swoft\Bean\Annotation\Inject;use Swoft\Auth\AuthManager;use Swoft\Auth\Bean\AuthSession;use Swoft\Auth\Mapping\AuthManagerInterface;/* * @Bean() * @package App\Services /class AuthManagerService extends AuthManager implements AuthManagerInterface{ /* * 缓存类 * @var string / protected $cacheClass = Redis::class; /* * jwt 具有自包含的特性 能自己描述自身何时过期 但只能一次性签发 * 用户主动注销后 jwt 并不能立即失效 所以我们可以设定一个 jwt 键名的 ttl * 这里使用是否 cacheEnable 来决定是否做二次验证 * 当获取token并解析后,token 的算法层是正确的 但如果 redis 中的 jwt 键名已经过期 * 则可认为用户主动注销了 jwt,则依然认为 jwt 非法 * 所以我们需要在用户主动注销时,更新 redis 中的 jwt 键名为立即失效 * 同时对 token 刷新进行验证 保证当前用户只有一个合法 token 刷新后前 token 立即失效 * @var bool 开启缓存 / protected $cacheEnable = true; // token 有效期 7 天 protected $sessionDuration = 86400 * 7; /* * 定义登录认证方法 调用 Swoft的AuthManager@login 方法进行登录认证 签发token * @param string $account * @param string $password * @return AuthSession / public function auth(string $account, string $password): AuthSession { // AuthLogic 需实现 AccountTypeInterface 接口的 login/authenticate 方法 return $this->login(AuthLogic::class, [ ‘account’ => $account, ‘password’ => $password ]); }}App\Controllers\AuthController处理用户的登录请求<?php/* * Created by PhpStorm. * User: big_cat * Date: 2018/12/10 0010 * Time: 17:05 /namespace App\Controllers;use App\Services\AuthManagerService;use Swoft\Http\Message\Server\Request;use Swoft\Http\Server\Bean\Annotation\Controller;use Swoft\Http\Server\Bean\Annotation\RequestMapping;use Swoft\Http\Server\Bean\Annotation\RequestMethod;use Swoft\Bean\Annotation\Inject;use Swoft\Bean\Annotation\Strings;use Swoft\Bean\Annotation\ValidatorFrom;/* * 登录认证模块 * @Controller("/v1/auth") * @package App\Controllers /class AuthController{ /* * 用户登录 * @RequestMapping(route=“login”, method={RequestMethod::POST}) * @Strings(from=ValidatorFrom::POST, name=“account”, min=6, max=11, default="", template=“帐号需{min}{max}位,您提交的为{value}”) * @Strings(from=ValidatorFrom::POST, name=“password”, min=6, max=25, default="", template=“密码需{min}{max}位,您提交的为{value}”) * @param Request $request * @return array / public function login(Request $request): array { $account = $request->input(‘account’) ?? $request->json(‘account’); $password = $request->input(‘password’) ?? $request->json(‘password’); // 调用认证服务 - 登录&签发token $session = $this->authManagerService->auth($account, $password); // 获取需要的jwt信息 $data_token = [ ’token’ => $session->getToken(), ’expired_at’ => $session->getExpirationTime() ]; return [ “err” => 0, “msg” => ‘success’, “data” => $data_token ]; }}POST /v1/auth/login 的结果{ “err”: 0, “msg”: “success”, “data”: { “token”: “eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJBcHBcXE1vZGVsc1xcTG9naWNcXEF1dGhMb2dpYyIsInN1YiI6IjgwIiwiaWF0IjoxNTUxNjAyOTk4LCJleHAiOjE1NTIyMDc3OTgsImRhdGEiOnsidWlkIjo4MH19.u2g5yU9ir1-ETVehLFIIZZgtW7u9aOvH2cndMsIY98Y”, “expired_at”: 1552207798 }}这里提及一下为什么要提供在服务端缓存token的选项$cacheEnable。普通的token不像jwt具有自我描述的特性,我们为维护token的有效期只能在服务端缓存其有效期,防止过期失效的token被滥用。jwt可以自我描述过期时间,为什么也要缓存呢?因为jwt自身的描述是只读的,即我们无法让jwt提前过期失效,如果用户退出登录,则销毁token是个不错的安全开发习惯,所以只有在服务端也维护了一份jwt的过期时间,用户退出时过期此token,那么就可以自由控制jwt的过期时间。/* * @param string $token * @return bool /public function authenticateToken(string $token): bool{ … // 如果开启了服务端缓存选项 则验证token是否过期 可变向控制jwt的有效期 if ($this->cacheEnable === true) { try { $cache = $this->getCacheClient() ->get($this->getCacheKey($session->getIdentity(), $session->getExtendedData())); if (! $cache || $cache !== $token) { throw new AuthException(ErrorCode::AUTH_TOKEN_INVALID); } } catch (InvalidArgumentException $e) { $err = sprintf(‘Identity : %s ,err : %s’, $session->getIdentity(), $e->getMessage()); throw new AuthException(ErrorCode::POST_DATA_NOT_PROVIDED, $err); } } $this->setSession($session); return true;}token 解析、验证token的解析及合法性验证实现流程,注意只是验证token的合法性,即签名是否正确,签发者,签发对象是否合法,是否过期。并未对 token 的访问权限做认证。使用到的组件及服务:#调用token拦截服务尝试获取token,并调用token管理服务做解析及合法性验证Swoft\Auth\Middleware\AuthMiddleware#token拦截服务``Swoft\Auth\Mapping\AuthorizationParserInterface::class#token拦截服务提供者,根据token类型调用相应的token解析器``Swoft\Auth\Parser\AuthorizationHeaderParser#token管理服务,由token管理服务提供者提供基础服务,被token解析器调用Swoft\Auth\Mapping\AuthManagerInterface::class#token管理服务提供者,负责签发、解析、合法性验证Swoft\Auth\AuthManagerSwoft\Auth\Middleware\AuthMiddleware负责拦截请求并调用token解析及验证服务。会尝试获取请求头中的Authorization字段值,根据类型Basic/Bearer来选择相应的权限认证服务组件对token做合法性的校验并生成token会话。但并不涉及业务访问权限ACL的验证,即只保证某个token 是本平台合法签发的,不保证此token对当前资源有合法的访问权限。如果Authorization为空的话则视为普通请求。执行流程:Swoft\Auth\Middleware\AuthMiddleware调用 Swoft\Auth\Mapping\AuthorizationParserInterface::class 服务,服务具体由 Swoft\Auth\Parser\AuthorizationHeaderParser实现。服务AuthorizationHeaderParser尝试获取请求头中的Authorization字段值,如果获取到token,则根据token的类型:BasicorBearer来调用具体的解析器。Basic的解析器为`Swoft\Auth\Parser\Handler::BasicAuthHandler,Bearer的解析器为 Swoft\Auth\Parser\Handler::BearerTokenHandler,下面我们具体以Bearer模式的jwt为示例。在获取到类型为Bearer的token后,BearerTokenHandler将会调用Swoft\Auth\Mapping\AuthManagerInterface::class服务的authenticateToken方法来对token进行合法性的校验和解析,即判断此token的签名是否合法,签发者是否合法,签发对象是否合法(注意:调用了App\Models\Logic\AuthLogic::authenticate方法验证),是否过期等。token解析验证非法,则抛出异常中断请求处理。token解析验证合法,则将payload载入本次会话并继续执行。所以我们可以将此中间件注册到全局,请求携带token则解析验证,不携带token则视为普通请求。#config/beans/base.phpreturn [ ‘serverDispatcher’ => [ ‘middlewares’ => [ … \Swoft\Auth\Middleware\AuthMiddleware::class ], … ], // token签发及合法性验证服务 \Swoft\Auth\Mapping\AuthManagerInterface::class => [ ‘class’ => \App\Services\AuthManagerService::class ],];<?phpnamespace AppModelsLogic;use SwoftAuthBeanAuthResult;use SwoftAuthMappingAccountTypeInterface;class AuthLogic implements AccountTypeInterface{…/* * 验证签发对象是否合法 这里我们简单验证签发对象是否为本平台用户 * $identity 即 jwt 的 sub 字段 * @override Swoft\Auth\Mapping\AccountTypeInterface * @param string $identity token sub 字段 * @return bool /public function authenticate(string $identity): bool{ return $this->userDao->exists($identity);}}acl鉴权token 虽然经过了合法性验证,只能说明token是本平台签发的,还无法判断此token是否有权访问当前业务资源,所以我们还要引入Acl认证。使用到的组件及服务:#Acl认证中间件Swoft\Auth\Middleware\AclMiddleware#用户业务权限auth服务Swoft\Auth\Mapping\AuthServiceInterface::class#token会话访问组件Swoft\Auth\AuthUserServiceSwoft\Auth\Middleware\AclMiddleware中间件会调用Swoft\Auth\Mapping\AuthServiceInterface::class服务,此服务主要用于Acl认证,即验证当前请求是否携带了合法token,及token是否对当前资源有访问权限。Swoft\Auth\Mapping\AuthServiceInterface::class服务由框架的Swoft\Auth\AuthUserService组件实现获取token会话的部分功能,auth方法则交由用户层重写,所以我们需继承Swoft\Auth\AuthUserService并根据自身业务需求实现auth方法。在继承了Swoft\Auth\AuthUserService的用户业务认证组件中,我们可以尝试获取token会话的签发对象及payload数据:getUserIdentity/getUserExtendData。然后在auth方法中判断当前请求是否有token会话及是否对当前资源有访问权限,来决定返回true or false给AclMiddleware中间件。AclMiddleware中间件获取到用户业务下的auth为false(请求没有携带合法token 401 或无权访问当前资源 403),则终端请求处理。AclMiddleware中间件获取到在用户业务下的auth为true,则说明请求携带合法token,且token对当前资源有权访问,继续请求处理。config/bean/base.phpreturn [ ‘serverDispatcher’ => [ ‘middlewares’ => [ …. //系统token解析中间件 \Swoft\Auth\Middleware\AuthMiddleware::class, … ] ], // token签发及合法性验证服务 \Swoft\Auth\Mapping\AuthManagerInterface::class => [ ‘class’ => \App\Services\AuthManagerService::class ], // Acl用户资源权限认证服务 \Swoft\Auth\Mapping\AuthServiceInterface::class => [ ‘class’ => \App\Services\AclAuthService::class, ‘userLogic’ => ‘${’ . \App\Models\Logic\UserLogic::class . ‘}’ // 注入UserLogicBean ],];App\Services\AclAuthService对token做Acl鉴权。<?phpnamespace App\Services;use Swoft\Auth\AuthUserService;use Swoft\Auth\Mapping\AuthServiceInterface;use Psr\Http\Message\ServerRequestInterface;/* * Bean 因在 config/beans/base.php 中已经以参数配置的方式注册,故此处不能再使用Bean注解声明 * Class AclAuthService * @package App\Services /class AclAuthService extends AuthUserService implements AuthServiceInterface{ /* * 用户逻辑模块 * 因本模块是以参数配置的方式注入到系统服务的 * 所以其相关依赖也需要使用参数配置方式注入 无法使用Inject注解声明 * @var App\Models\Logic\UserLogic / protected $userLogic; /* * 配合 AclMiddleware 中间件 验证用户请求是否合法 * true AclMiddleware 通过 *false AclMiddleware throw AuthException * @override AuthUserService * @param string $requestHandler * @param ServerRequestInterface $request * @return bool */ public function auth(string $requestHandler, ServerRequestInterface $request): bool { // 签发对象标识 $sub = $this->getUserIdentity(); // token载荷 $payload = $this->getUserExtendData(); // 验证当前token是否有权访问业务资源 aclAuth为自己的认证逻辑 if ($this->aclAuth($sub, $payload)) { return true; } return false; }} ...

March 5, 2019 · 4 min · jiezi

Vue笔记(五)——Token&生命周期

Token定义:令牌,一般用于用户身份验证Token的特点随机性不可预测性时效性无状态跨域node所需模块jsonwebtoken生成并加密Token// 设置对象let user = {};// 加密密钥let secret = ‘0000’;let token = jwt.sign(user,secret,{ ’expiresIn’:60*60 // 设置过期时间});解密Token// 获取前端发送的tokenlet token = req.headers[‘auth’];jwt.verify(token,‘0000’,(error,result)=>{ if(error){ res.send({false,{},‘unauth’}); }else{ res.send({true,{},result); }})生命周期Vue提供了很多钩子函数给我们在不同的时刻操作不同的代码beforeCreate:属性和方法初始化之前create:属性和方法初始化完成beforeMount:宿主元素挂载前mounted:宿主元素挂在完成beforeUpdate:属性和方法更新之前updated:属性和方法更新完成beforeDestory:销毁组件之前destoryed:组件销毁完成

February 28, 2019 · 1 min · jiezi

InvalidClaimException: The Token can't be used before

jwt token错误linux服务器上最近使用jwt token的时候遇到了一个奇怪的问题:InvalidClaimException: The Token can’t be used before 某个时间测试库完全没问题,正式库只使用一台也没问题,但是一旦启用多台服务器就会报这个错误。经分析发现,多台正式服务器的系统时间是不一样的,有一定的差值。生成token的服务器比使用token的服务器时间快几十秒,生成token后,请求分发到时间慢的服务器上导致时间还没到token的开始时间,才报这个错误。同步服务器时间: nptdate -u ntp1.aliyun.com注意123端口必须可以使用或者通过date设置系统时间,具体的可以百度。

February 28, 2019 · 1 min · jiezi

彻底弄懂session,cookie,token

session,cookie和token究竟是什么简述我在写之前看了很多篇session,cookie的文章,有的人说先有了cookie,后有了session。也有人说先有session,后有cookie。感觉都没有讲的很清楚,泛泛而谈。希望本篇文章对大家有所帮助注:本文需要读者有cookie,session,token的相关基础知识。http是一个无状态协议什么是无状态呢?就是说这一次请求和上一次请求是没有任何关系的,互不认识的,没有关联的。这种无状态的的好处是快速。坏处是假如我们想要把www.zhihu.com/login.html和www.zhihu.com/index.html关联起来,必须使用某些手段和工具cookie和session由于http的无状态性,为了使某个域名下的所有网页能够共享某些数据,session和cookie出现了。客户端访问服务器的流程如下首先,客户端会发送一个http请求到服务器端。服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下,具体请看Cookie详解Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie服务器接收请求,分解cookie,验证信息,核对成功后返回response给客户端注意cookie只是实现session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用cookie后还有其他方法存储,比如放在url中现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用用session只需要在客户端保存一个id,实际上大量数据都是保存在服务端。如果全部用cookie,数据量大的时候客户端是没有那么多空间的。如果只用cookie不用session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大小结简而言之, session 有如用户信息档案表, 里面包含了用户的认证信息和登录状态等信息. 而 cookie 就是用户通行证tokentoken 也称作令牌,由uid+time+sign[+固定参数]token 的认证方式类似于临时的证书签名, 并且是一种服务端无状态的认证方式, 非常适合于 REST API 的场景. 所谓无状态就是服务端并不会保存身份认证相关的数据。组成uid: 用户唯一身份标识time: 当前时间的时间戳sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查库存放token在客户端一般存放于localStorage,cookie,或sessionStorage中。在服务器一般存于数据库中token认证流程token 的认证流程与cookie很相似用户登录,成功后服务器返回Token给客户端。客户端收到数据后保存在客户端客户端再次访问服务器,将token放入headers中服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码token可以抵抗csrf,cookie+session不行假如用户正在登陆银行网页,该网页未对csrf攻击进行防护。攻击者就可以注入一张图片,该图片src为http://www.bank.com/api/transfer?count=1000&to=Tom。倘若是session+cookie,用户打开网页的时候就已经转给Tom1000元了。因为session一旦建立,当前域页面以及该页面路径以下所有页面都共享cookie。在img请求的瞬间,cookie会被浏览器自动添加到请求头中。但token不同,开发者在每次发起请求时手动将 Token 添加到请求中。即打开页面请求img时,该请求头中没有token分布式情况下的session和token我们已经知道session时有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,session就会面对负载均衡问题。负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享seesion。这个问题也可以将session存在一个服务器中来解决,但是就不能完全达到负载均衡的效果。当今的几种解决session负载均衡的方法。而token是无状态的,token字符串里就保存了所有的用户信息客户端登陆传递信息给服务端,服务端收到后把用户信息加密(token)传给客户端,客户端将token存放于localStroage等容器中。客户端每次访问都传递token,服务端解密token,就知道这个用户是谁了。通过cpu加解密,服务端就不需要存储session占用存储空间,就很好的解决负载均衡多服务器的问题了。这个方法叫做JWT(Json Web Token)总结session存储于服务器,可以理解为一个状态列表,拥有一个唯一识别符号sessionId,通常存放于cookie中。服务器收到cookie后解析出sessionId,再去session列表中查找,才能找到相应session。依赖cookiecookie类似一个令牌,装有sessionId,存储在客户端,浏览器通常会自动添加。token也类似一个令牌,无状态,用户信息都被加密到token中,服务器收到token后解密就可知道是哪个用户。需要开发者手动添加。jwt只是一个跨域认证的方案参考文章Cookie、Session、Token那点事儿cookie,token验证的区别有了cookie为什么需要sessionCSRF Token的设计是否有其必要性cookie,token,session三者的问题和解决方案负载均衡集群中的session解决方案JWT介绍Json Web Token 入门教程谢谢本文如有错误,欢迎留言讨论

January 9, 2019 · 1 min · jiezi

关于JWT(Json Web Token)的思考及使用心得

什么是JWT?JWT(Json Web Token)是一个开放的数据交换验证标准rfc7519(https://tools.ietf.org/html/r…,一般用来做轻量级的API鉴权。由于许多API接口设计是遵循无状态的(比如Restful),所以JWT是Cookie Session这一套机制的替代方案。组成JWT由三部分组成头部(header)、载荷(payload)、签名(signature)。头部定义类型和加密方式;载荷部分放不是很重要的数据;签名使用定义的加密方式加密base64后的header和payload和一段你自己的加密key。最后的token由base64(header).base64(payload).base64(signatrue)组成。使用方式平时需要鉴权的接口需要传这个token,可以post字段提交,但是一般建议放在header头中 ,因为JWT一般配合https使用,这样就万无一失。为什么安全?首先token是服务端签发,然后验证时是用同样加密方式把header、payload和key再加密遍 然后看是不是和签名一致 如果不一致就说明token是非法的 这里主要靠的是加密(比如HS256)难以被攻破 至少目前吧 另外不得不说这里的加密对服务器来说是一个开销 这也是JWT的缺点使用我很早就听说过JWT 但那时候还没用上 感觉近几年前后端分离思想加速了JWT的使用 MVC前置到前端(VUE、REACT)后端只用提供API API强调无状态 自然而然使用了JWT这套方案之前做小程序时 没有绝对使用JWT这套方案 我把它简化了下最近开发的一套项目用的是Laravel做后端提供API服务,所以自然而然使用了JWT,使用的扩展是tymon/jwt-auth 关于这套扩展机制的具体使用可以参考这篇文章 写的很不错:https://laravel-china.org/art…项目使用时的具体细节JWT三个时间概念JWT有三个时间概念: 过期时间 宽限时间 刷新时间上面那文章说token过了过期时间是不可刷新的,但其实是可以刷新的,我这边使用时可以(开启了黑名单机制和1min宽限时间) 但是过了刷新时间不能刷新这是肯定的token刷新可以借用laravel的中间件实现自动刷新 服务端判断过期了但是在刷新时间内主动刷新一次 并把新的token在Header头中返回给客户端记住我功能我感觉定义刷新时间(比如一个月) + token自动刷新机制这一套就是记住我功能不能主动销毁Token默认JWT这套方案好像不可以主动剔除用户的,因为服务端不会存token,只是验证。这和session不一样。 但是我感觉可以借用黑名单机制 来判断 中间件中判断该token在剔除黑名单中就销毁token并返回鉴权失败单点登录结合缓存比如redis存客户端标识是可以的多套用户权限机制比如客户端有用户端和商家端两套用户机制(不同表),可以在auth.php定义两个guard 分别指定对应的model Login逻辑单独写 比如auth(‘userApi’) auth(‘corpApi’) 可以拿到不同的guard 刷新中间件是可以共用的 不过router中的middleware方法可以指定具体的middleware为什么不同权限体系的刷新中间件可以公用感觉是jwt-auth实现了这机制 验证token时会根据sub字段判断 我base64decode两个token的payload后发现sub一个是1 一个是2如果有问题欢迎指正!本文最早发布于Rootrl’s blog https://rootrl.github.io/2018…

October 4, 2018 · 1 min · jiezi