JWT RefreshToken 实践

Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。 详细介绍可以查看这篇文章 理解JWT(JSON Web Token)认证及实践 JWT 特点 优点 体积小,因而传输速度快 传输方式多样,可以通过URL/POST参数/HTTP头部等方式传输 严格的结构化。它自身(在 payload 中)就包含了所有与用户相关的验证消息,如用户可访问路由、访问有效期等信息,服务器无需再去连接数据库验证信息的有效性,并且 payload 支持为你的应用而定制化。 支持跨域验证,可以应用于单点登录。 存在的问题 JWT 自身(在 payload 中)就包含了所有与用户相关的验证消息,所以通常情况下不需要保存。这种设计存在几个问题: Token不能撤销–客户端重置密码后之前的JWT依然可以使用(JWT 并没有过期或者失效 不支持refresh token,JWT过期后需要执行登录授权的完整流程 无法知道用户签发了几个JWT 针对第一个问题,可能的解决方法有: 保存JWT到数据库(或Redis),这样可以针对每个JWT单独校验 在重置密码等需要作废之前全部JWT时,把操作时间点记录到数据库(或Redis),校验JWT时同时判断此JWT创建之后有没有过重置密码等类似操作,如果有校验不通过 当然,这种解决方法都会多一次数据库请求,JWT自身可校验的优势会有所减少,同时也会影响认证效率。 这篇文章主要介绍解决第二个问题(不支持refresh token)的思路。 refresh token refresh token是OAuth2 认证中的一个概念,和OAuth2 的access token 一起生成,表示更新令牌,过期所需时间比access toen 要长,可以用来获取下一次的access token。 如果JWT 需要添加 refresh token支持,refresh token需要满足的条件有一下几项: 和JWT一起生成返回给客户端 有实效时间,有效时间比JWT要长 只能用来换取下一次JWT,不能用于访问认证 不能重复使用(可选) refresh token 获取流程 refresh token 使用流程 代码示例 import jwt import time # 使用 sanic 作为restful api 框架 def create_token(account_id, username): payload = { "iss": "gusibi.mobi", "iat": int(time.time()), "exp": int(time.time()) + 86400 * 7, "aud": "www.gusibi.mobi", "sub": account_id, "username": username, "scopes": ['open'] } token = jwt.encode(payload, 'secret', algorithm='HS256') payload['grant_type'] = "refresh" refresh_token = jwt.encode(payload, 'secret', algorithm='HS256') return True, { 'access_token': token, 'account_id': account_id, "refresh_token": refresh_token } # 验证refresh token 出否有效 def verify_refresh_token(token): payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256']) # 校验token 是否有效,以及是否是refresh token,验证通过后生成新的token 以及 refresh_token if payload and payload.get('grant_type') == 'refresh': # 如果需要标记此token 已经使用,需要借助redis 或者数据库(推荐redis) return True, payload return False, None # 验证token 是否有效 def verify_bearer_token(token): # 如果在生成token的时候使用了aud参数,那么校验的时候也需要添加此参数 payload = jwt.decode(token, 'secret', audience='www.gusibi.com', algorithms=['HS256']) # 校验token 是否有效,以及不能是refresh token if payload and not payload.get('grant_type') == 'refresh': return True, payload return False, None 参考链接 理解JWT(JSON Web Token)认证及实践 理解OAuth 2.0[1] References [1] 理解OAuth 2.0: http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html ...

2019-04-29 · 1 min · 204 words

CSS入门指南-3:定位元素

这是《CSS设计指南》的读书笔记,用于加深学习效果。 前一篇CSS入门指南-2:盒子模型、浮动和清除介绍了css盒子模型、浮动和清除,这一篇介绍 css元素的定位。 定位(position) CSS 布局的核心是 position 属性,对元素盒子应用这个属性,可以相对于它在常规文档流中的位置重新定位。 position 属性有4个值:static、relative、absoulte、fixed,默认值为 static。 接下来我会用以下四个段落来逐个说明这些属性是什么意思。 <p id="first">First Paragraph</p> <p id="Second">Second Paragraph</p> <p id="specialpara">Third Paragraph</p> <p id="fourth">First Paragraph</p> 静态定位(static) 我们先看一下四个段落都采用静态定位的效果。 静态定位下,每个元素在处在常规文档流中,它们都是块级元素,所以会在页面中自上而下地堆叠。 相对定位(relative) 现在我把第三段的 position 属性设置为 relative。 p#specialpara { position: relative; top: 25px; left: 30px; } 因为相对定位相对的是它原来在文档流中的位置(默认位置),所以如果只设置 position 样式不会有任何变化。这里我同时设置了 top 和 left 属性来改变它的位置。 现在它的效果如图所示: 现在,第三段从原来的元素(body)中挣脱了出来,与它在文档中的默认位置相比向下移动了25像素,向右移动了30像素。 需要注意的是,除了这个元素自己相对于原始位置挪动了一下以外,页面没有任何改变。这个元素原来占据的空间没有动,其他元素也没动。 这时,如果不想第四段被它挡住,可以给第四段设置一个 margin-top 值。 绝对定位(absoulte) 绝对定位跟静态定位和相对定位相比,它会把元素彻底从文档流中拿出来。 我们把 position 改为绝对定位看一下: p#specialpara { position: absoulte; top: 25px; left: 30px; } 效果如图: 可以看到,第三段原来的位置被回收了。这说明绝对定位的元素脱离了常规文档流,它现在是相对于顶级元素 body 在定位。 现在就涉及到一个概念:定位上下文,这个后边说,先继续看最后一种定位方式:固定定位。 盒子位移属性是如何工作? 盒子的位移属性有四个“top、right、bottom和left”,用来指定元素的定位位置和方向。这些属性只能在元素的“position”属性设置了“relative、absolute和fixed”属性值,才生效。 对于相对定位元素,这些属性的设置让元素从默认位置移动。例如,top设置一个值“20px”在一个相对定位的元素上,这个元素会在原来位置向下移动“20px”。 ...

2017-07-26 · 2 min · 290 words

理解JWT(JSON Web Token)认证及实践

最近想做个小程序,需要用到授权认证流程。以前项目都是用的 OAuth2 认证,但是Sanic 使用OAuth2 不太方便,就想试一下 JWT 的认证方式。 这一篇主要内容是 JWT 的认证原理,以及python 使用 jwt 认识的实践。 几种常用的认证机制 HUGOMORE42 HTTP Basic Auth HTTP Basic Auth 在HTTP中,基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式,通常用户名和明码会通过HTTP头传递。 在发送之前是以用户名追加一个冒号然后串接上口令,并将得出的结果字符串再用Base64算法编码。例如,提供的用户名是Aladdin、口令是open sesame,则拼接后的结果就是Aladdin:open sesame,然后再将其用Base64编码,得到QWxhZGRpbjpvcGVuIHNlc2FtZQ==。最终将Base64编码的字符串发送出去,由接收者解码得到一个由冒号分隔的用户名和口令的字符串。 优点 基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。 缺点 由于用户名和密码都是Base64编码的,而Base64编码是可逆的,所以用户名和密码可以认为是明文。所以只有在客户端和服务器主机之间的连接是安全可信的前提下才可以使用。 接下来我们看一个更加安全也适用范围更大的认证方式 OAuth。 OAuth OAuth 是一个关于授权(authorization)的开放网络标准。允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。现在的版本是2.0版。 严格来说,OAuth2不是一个标准协议,而是一个安全的授权框架。它详细描述了系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)之间怎么实现相互认证。 名词定义 Third-party application: 第三方应用程序,又称"客户端"(client) HTTP service:HTTP服务提供商 Resource Owner:资源所有者,通常称"用户"(user)。 User Agent:用户代理,比如浏览器。 Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。 Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。 OAuth 2.0 运行流程如图: (A)用户打开客户端以后,客户端要求用户给予授权。 (B)用户同意给予客户端授权。 (C)客户端使用上一步获得的授权,向认证服务器申请令牌。 (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。 (E)客户端使用令牌,向资源服务器申请获取资源。 (F)资源服务器确认令牌无误,同意向客户端开放资源。 优点 快速开发 实施代码量小 维护工作减少 如果设计的API要被不同的App使用,并且每个App使用的方式也不一样,使用OAuth2是个不错的选择。 缺点: OAuth2是一个安全框架,描述了在各种不同场景下,多个应用之间的授权问题。有海量的资料需要学习,要完全理解需要花费大量时间。 OAuth2不是一个严格的标准协议,因此在实施过程中更容易出错。 了解了以上两种方式后,现在终于到了本篇的重点,JWT 认证。 ...

2017-07-23 · 2 min · 398 words

CSS入门指南-2:盒子模型、浮动和清除

这是CSS设计指南的读书笔记,用于加深学习效果。 上一篇介绍了css 的工作原理,这一篇主要介绍盒子模型和浮动。 盒子模型 所谓盒子模型,就是浏览器为页面中的每个HTML元素生成的矩形盒子。这些盒子们都要按照可见版式模型在页面上排布。 HUGOMORE42 可见的页面版式主要由三个属性控制:position、display和float。 position:控制页面上元素的位置关系 display:控制元素是堆叠、并排还是不在页面出现 float:提供控制的方式,以便吧元素组成多栏布局 元素盒子的属性可以分成三组: 边框(board)。可以甚至边框的宽窄、样式和颜色 内边距(padding)。可以甚至盒子内容区与边框的间距 外边距(margin)。可以设置盒子与相邻元素的间距 元素盒子还有一个背景层,可以改变颜色,也可以添加图片。 简写样式 CSS为边框、内边距和外边距分别规定了简写属性,每个简写声明中,属性值得顺序都是上、右、下、左。 比如: { margin-top: 5px; margin-right: 10px; margin-bottom: 12px; margin-left: 8px; } 使用简写则为这样: { margin: 12px 10px 12px 8px; } 如果有一个值没写,那么则使用对边的值。 比如: {margin: 12px 10px 12px;} /*等同于*/ { margin: 12px 10px 12px 10px; } 如果只写一个值,则4个边都取这个值。 {margin: 12px;} /*等同于*/ {margin: 12px 12px 12px 12px;} 另外每个盒子的属性也分三个粒度,这三个粒度从一般到特殊分别举例如下: { border: 2px dashed red; } 混合使用三种粒度的简写属性达成设计目标是很常见的。比如,想为盒子的上边和下边添加4像素的红色边框,为左边添加1像素宽的红色边框,而右边没有。可以这么写: {border: 4px solid red;} /* 先给4条边设置相同的样式*/ {border-left-width: 1px;} /* 修改左边框宽度*/ {border-fight: none;} /*移出右边框*/ 盒子边框 border 有三个相关属性。 宽度(border-width)。可以使用thin、medium和thick等文本值,也可以使用除百分比和负值之外的任何绝对值。 样式(border-style)。有none、hidden、dotted、dashed等文本值。 颜色(border-color)。可以使用任意颜色值,包括RGB、HSL、十六进制颜色值和颜色关键字。 盒子内边距 内边距是盒子内容区与盒子边框之间的距离。 上图的样式为: p { font: 16px helvetica, sans-serif; width: 220px; border: 2px solid red; background-color: #caebff; } 可以看到在没有设定内边距的情况下,内容紧挨着边框。 设定边框后: ...

2017-07-19 · 3 min · 571 words

CSS入门指南-1:工作原理

这是CSS设计指南的读书笔记,用于加深学习效果。 最近想做一个小程序,前端是必修课,那就从css开始吧。 css 工作原理 每个html元素都有一组样式属性,可以通过css来设定。当html元素的同一个样式属性有多种样式值的时候,css就要靠层叠机智来决定最终应用哪种样式。 HUGOMORE42 css规则 规则实际上是一条完整的css指令,规则声明了要修改的元素和要应用给改元素的样式。 为文档添加样式的三种方法: 写在元素标签里(也叫行内样式,只能影响它所在的标签,会覆盖嵌入样式和链接样式) 写在<style> 标签里(也就嵌入样式,应用范围仅限于当前页面,页面样式会覆盖外部样式表中的样式,但会被行内样式覆盖) 写在单独css样式表中(也叫链接样式,样式表是一个扩展名为.css 的文件,可以在任意多个HTML页面链接同一个样式表文件。链接样式的作用范围是整个网站) 除了这三种为页面添加样式的方法,还有一种在样式表中链接其他样式表的方法,使用@import 指令:例如 @import url(css/styles.css) @import 指令必须出现在样式表中其他样式之前,否则@吹灭;@import引用的样式表不会被加载。 对这个基本的结构有三种方法可以进行扩展 **第一种方法:**多个声明包含在一条规则里。 p {color: red; font-size: 12px; font-weight: bold;} **第二种方法:**多个选择器组合在一起。例如:如果想让<h1>、<h2>和<h3>的文本都变成蓝色粗体可以这么写: h1 {color: blue; font-weight: bold;} h2 {color: blue; font-weight: bold;} h3 {color: blue; font-weight: bold;} 也可以这么写: h1, h2, h3 {color: blue; font-weight: bold;} 分组选择符以逗号作为分隔符 第三种方法: 多条规则应用给一个选择符。 例如,写完上边的规则,还想把h3变成斜体,那么可以再为h3单独写一条规则: h1, h2, h3 {color: blue; font-weight: bold;} h3 {font-style: italic;} 选择特定元素的选择符 用于选择特定元素的操作符有三种 上下文选择符。基于祖先或者同胞元素选择一个元素。 ID和类选择符。基于id和class属性的值选择元素。 属性选择符。基于属性的有无和特征选择元素。 上下文选择符 比如我们想给article中的段落设置不同的字号,可以使用上下文选择符来解决。 上下文选择符的格式如下: 标签1 标签2 {声明} 其中标签2 是我们要选择的目标,而且只有在 标签1是其祖先元素的情况下才会被选中。 上下文选择符,叫后代组合式选择符,就是一组以空格分隔的标签名。用于选择作为特定祖先元素后代的标签。 article p {font-weight: bold;} 上边例子中,只有article后代的p元素才会应用后边的样式。 上下文选择符以空格作为分隔符 特殊的上下文选择符 子选择符 > 格式如下: 标签1 > 标签2 标签1 必须是 标签2 的父元素,不能是其它的祖先元素。 ...

2017-05-27 · 3 min · 451 words