After successfully logging in, how to manage user identities in a secure manner is a key point that every web development project will encounter. This is related to the maintenance efficiency of the front-end code and the security control of the back-end interface. Negligence in any aspect may lead to subsequent complex problems.
Extract public templates to improve maintenance efficiency
When faced with multiple pages with duplicate code, the first step is to optimize the project template. Take the common header navigation menu and encapsulated axios request logic as an example. If these components are scattered in each independent template, then later modifications will be extremely cumbersome. Developers can create a common header template file, such as header.html , under the project's view directory (such as views ).
In this public template, you can write a navigation bar with HTML structure and style. Later, in the page templates that need to be applied to the navigation, such as the login page login.html , this common part is introduced with the help of the template engine's syntax of inclusion or inheritance. This method of management can not only reduce the amount of code involved, but also enable adjustments to global styles or functions to be achieved in one place, greatly improving the maintainability of the project.
${ render "header.html" }Plan template structure for future expansion
const myaxios = function (url, type, data = {}) {
return new
Promise((resolve) => {
if (type === "get" || type === "delete") {
axios({
method: type,
url: url,
params: data
}).then((result) => {
resolve(result.data);
});
} else {
const params = new URLSearchParams();
for (var key in data) {
params.append(key,data[key]);
}
axios({
method: type,
url: url,
data:params
}).then((result) => {
resolve(result.data);
});
}
});
}
Now this project may only have a home page, login page and a few management pages. However, as the functions continue to increase, the number of templates will grow rapidly. In this case, it is extremely important to plan and develop the hierarchical structure of the template in advance. You can create a basic layout template to define the frame structure of the entire page, including.区域、公共脚本和样式引入点。
${ render "myaxios.html" }用于其他业务的页面模板,会沿袭这个基础布局,只不过去填充自身特有的内容切块。比如说,用户管理页面会继承该基础布局,只是覆盖其中的主要内容范围。这样的结构保证了整个网站风格统一,同期把公共依赖的管理实现集中化,防止了在几十个页面里逐个展开添加亦或者更新第三方库所涉及的引用。
.
├── README.md
├── assets
│ ├── css
│ │ └── style.css
│ └── js
│ ├── axios.js
│ └── vue.js
├── database
│ └── database.go
├── favicon.ico
├── go.mod
├── go.sum
├── handler
│ ├── admin.go
│ └── user.go
├── main.go
├── model
│ └── model.go
├── mytool
│ └── mytool.go
├── tmp
│ └── runner-build
└── views
├── admin
│ └── user.html
├── admin_header.html
├── header.html
├── index.html
├── myaxios.html
├── signin.html
└── test.html<b>生成JWT令牌实现无状态认证</b>
使用者凭借账号密码以及验证码完成校验之后,系统必得生成一个凭证以供后续请求予以使用。标准JSON Web Token(JWT)是一种被广泛采用的标准。那是一个历经数字签名的包含了用户标识、签发时间等信息的JSON对象。于Go语言项目当中,能够借助
等包来生成。
{
"uid":1
}生成之际,得去定义一个结构体用作载荷,这载荷含有用户ID、过期时间等字段。要采用一个唯有服务器知晓的密钥,像一个复杂的字符串对令牌予以签名。签名选择的算法常常是HS256。如此便能生成一个类似
的字符串令牌,前端接收之后需妥善保存在本地。
<b>为令牌设置合理的有效期</b>
要出于安全方面的考量,所生成的JWT是一定要设置有效期的,比如说,能够把过期的时间设定为50分钟,这就表明了就算用户的令牌不小心出现了泄露的情状,攻击者也仅仅能在极为短暂的窗口期之内进行冒用,在载荷当中是会记录令牌的签发时间(
iat )以及过期时间( exp )的。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVaWQiOjEsImlhdCI6MTY2MTg0MjYxMCwiZXhwIjoxNjYxODQ1NjEwfQ.BXK9awvVCk7L3JAnDGt9z6U9TOjPCpI0AcHRu1eq_mo前端要是带着过期的令牌去访问接口,那后端校验就会失败。在这种情况下,前端应当去引导用户重新登录以获取新的令牌。对于那种需要长时间维持登录状态的应用而言,可以采用刷新令牌的机制,可是这会带来额外的复杂性,还需要更严谨周密的安全设计,初期的项目能够暂时不进行实现。
<b>使用中间件统一进行接口鉴权</b>
go get -u github.com/kataras/iris/v12/middleware/jwt在Iris或者类似的框架内,那个专门拦截HTTP请求绝佳位置是中间件。能够去编写一个鉴权这之中间件函数,而它核心方面的逻辑是这般的:从那个象
这样的请求头里提取出JWT令牌字符串,利用同一密钥并且凭借算法去验证它签名是否可生效,还要检查它是不是处在过期状态。
package mytool
import (
"crypto/md5"
"fmt"
"io"
"time"
"github.com/dchest/captcha"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/middleware/jwt"
)
var SigKey = []byte("signature_hmac_secret_shared_key")
type PlayLoad struct {
Uid uint
}
func GenerateToken(uid uint) string {
signer := jwt.NewSigner(jwt.HS256, SigKey, 50*time.Minute)
claims := PlayLoad{Uid: uid}
token, err := signer.Sign(claims)
if err != nil {
fmt.Println(err)
}
s := string(token)
return s
}验证被通过之后,能够从解码完成后的令牌载荷里解析得出用户ID,再一并将其存放进本次的请求上下文Context当中。如此一来,后续的业务处理函数便能够直接从上下文那儿获取获取当前登录用户的相关信息,而不必再去重复解析令牌。这致使所有需要登录的接口都能够借助一行代码予以统一添加这一层内容的保护。
<b>前端统一处理认证与请求</b>
登录成功之际,前端会于响应里获取JWT令牌,一般把它存于浏览器的
localStorage或者sessionStorage之中。此后,所有发向何处称之为后端API的请求,均得在HTTP请求头之际携带这个令牌。
可于前端封装的公共请求函数里,基于
the axios的那种封装,统一自存储读取令牌,而且要自动把它添加至每个请求的. At the same time, this public request process must also uniformly handle the token invalidation situation returned by the backend. If a 401 status code or the like appears, it must automatically jump to the login page to provide users with a consistent experience.
//登录动作
func Signin(ctx iris.Context) {
ret := make(map[string]interface{}, 0)
cid := ctx.PostValue("cid")
code := ctx.PostValue("code")
if captcha.VerifyString(cid, code) == false {
ret["errcode"] = 2
ret["msg"] = "登录失败,验证码错误"
ctx.JSON(ret)
return
}
db := database.Db()
defer func() {
_ = db.Close()
}()
Username := ctx.PostValue("username")
Password := ctx.PostValue("password")
user := &model.User{}
db.Where(&model.User{Username: Username, Password: mytool.Make_password((Password))}).First(&user)
if user.ID == 0 {
ret["errcode"] = 1
ret["msg"] = "登录失败,账号或者密码错误"
ctx.JSON(ret)
return
}
token := mytool.GenerateToken(user.ID)
fmt.Println(token)
ret["errcode"] = 0
ret["msg"] = "登录成功"
ret["username"] = user.Username
ret["token"] = token
ctx.JSON(ret)
}When developing separate front-end and back-end projects, how do you design the automatic renewal period mechanism of user tokens while ensuring security? You are welcome to share your practical experience in the comment area. If you feel that this article is helpful to you, please give it a like and support.
{
"errcode": 0,
"msg": "登录成功",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVaWQiOjEsImlhdCI6MTY2MTg0MzI5MSwiZXhwIjoxNjYxODQ2MjkxfQ.547z3nv4qj2-UeHTzfeG_qSsnFZD2DFyCP9gNZ-QiHA",
"username": "123"
}


