前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[golang] 实现 jwt 方式登录

[golang] 实现 jwt 方式登录

作者头像
逝兮诚
发布2023-02-26 14:54:55
9330
发布2023-02-26 14:54:55
举报
文章被收录于专栏:代码人生代码人生

1 Jwt 和 Session 登录方案介绍

JSON Web Token(缩写 JWT)是目前流行的跨域认证解决方案。

原理是生存的凭证包含标题 header,有效负载 payload 和签名组成。用户信息payload中,后端接收时只验证凭证是否有效,有效就使用凭证中的用户信息。

签名是通过标题 header,有效负载 payload 和密钥(后端保存,不可泄露)生成。

JWT 介绍:https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html JWT 实现:https://learnku.com/go/t/52399

登录替代方案(session)

常规传统登录方式使用 session,登录成功后端存储 sessionId 和对应的登录用户信息,返回 sessionId 给前端。前端将 sessionId 存在 cookie 中。前端发送请求时,附带传上 cookie 中的 sesssionId,后端接受 sessionId 时,查询 sessionId 是否存在对应用户信息,存在即说明登录成功。

缺点:

  1. 没有分布式架构,无法支持横向扩展,后端服务是有状态的。
  2. Session依赖Cookie,如果客户端禁用了Cookie,那么Session无法正常工作。
  3. Session依赖Cookie,Cookie无法防止CSRF(Cross Site Request Forgery):跨站请求伪造。

JWT 好处

  1. 后端服务是无状态服务,支持横向扩展。
  2. 基于 json,可以在令牌中自定义丰富内容,不依赖认证服务就可以完成授权。

Jwt 缺点

  1. token 过长
  2. token 一旦发出,无法销毁

2 Jwt 实现方案

JWT 登录方案包含登录,鉴权,续期三个逻辑,包含子需求有

后端:

  1. 登录生成 token
  2. 每次请求,验证 token 是否合法
  3. token 续期

前端:

  1. 登录密码 sha 256 加密
  2. 获取 token 并存储
  3. 每次请求,携带 token
  4. 每次请求,发现 token 不合法错误直接跳转登录
  5. 每次请求,查看 token 是否需要自动续期
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3 如何实现(后端 goframe,前端 acro design in vue)

登录实现

代码语言:javascript
复制
// 登录
func (C CUser) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) {
	role, tokenString, err := logic.User.Login(ctx, req.Username, req.Password)
	if err != nil {
		return
	}
	res = &v1.LoginRes{
		Token: tokenString,
		Role:  role,
	}
	return
}

// 登录
func (s *lUser) Login(ctx context.Context, username string, password string) (role string, token string, err error) {
	// 查询用户信息
	var user *entity.User
	err = dao.User.Ctx(ctx).Where(do.User{
		Passport: username,
		Password: password,
	}).Scan(&user)
	if err != nil {
		return
	}
	if user == nil {
		err = errors.New("账户或密码错误")
		return
	}
	// 生成 jwt token
	token, err = MyJwt.GenerateToken(ctx, username)
	if err != nil {
		return
	}
	return
}

// GenerateToken 生成 jwt 格式 token
func (lJwt) GenerateToken(ctx context.Context, username string) (token string, err error) {
	tokenHeader := jwt.NewWithClaims(jwt.SigningMethodHS256, &MyClaims{
		Username: username,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(ExpiresTime).Unix(),
		},
	})
	token, err = tokenHeader.SignedString(JwtSecret)
	return
}

鉴权实现

代码语言:javascript
复制
// 检测登录 token 是否合法
func Auth(r *ghttp.Request) {
    valid := logic.User.IsSignedIn(r.GetCtx(), r)
    if !valid {
        r.SetError(gerror.NewCode(gcode.New(50008, "请重新登录", "非法令牌或还未登录")))
        return
    }
    r.Middleware.Next()
}

// 查看是否登录
func (s *lUser) IsSignedIn(ctx context.Context, r *ghttp.Request) bool {
    token, exist := s.getToken(r)
    if !exist {
        return false
    }
    valid := MyJwt.Valid(r.Context(), token)
    return valid
}

// Valid 验证 token 是否合法
func (lJwt) Valid(ctx context.Context, token string) (valid bool) {
    var claims *MyClaims = &MyClaims{}
    tkn, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
        return JwtSecret, nil
    })
    if err != nil {
        valid = false
    } else {
        valid = tkn.Valid
    }
    return
}

后台签证续期逻辑

代码语言:javascript
复制
func (C CUser) Refresh(ctx context.Context, req *v1.RefreshReq) (res *v1.RefreshRes, err error) {
    newToken, err := logic.MyJwt.GenerateToken(ctx, logic.Ctx.Get(ctx).Username)
    res = &v1.RefreshRes{
        Token: newToken,
    }
    return
}

// GenerateToken 生成 jwt 格式 token
func (lJwt) GenerateToken(ctx context.Context, username string) (token string, err error) {
    tokenHeader := jwt.NewWithClaims(jwt.SigningMethodHS256, &MyClaims{
        Username: username,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: time.Now().Add(ExpiresTime).Unix(),
        },
    })
    token, err = tokenHeader.SignedString(JwtSecret)
    return
}

前端拦截器实现头添加 token 和续期操作

代码语言:javascript
复制
axios.interceptors.request.use(
  (config: AxiosRequestConfig) => {
  // let each request carry token
  // this example using the JWT token
  // Authorization is a custom headers key
  // please modify it according to the actual situation
  const token = getToken();
  // const tokenData = jwt.decode(token);

  // decodedHeader.exp
  if (token) {
  const decodedHeader = jwtDecode(token);
  const now = Date.parse(new Date().toString()) / 1000;
  // token 续期,如果 token 有效期小于60s,发起续期请求
  if (
  config.url !== '/api/user/refresh' &&
  decodedHeader.exp > now &&
  decodedHeader.exp - 60 < now
  ) {
  refreshToken()
  .then((res) => {
  setToken(res.data.token);
  })
  .finally();
  }
  // header 头携带 token
  if (!config.headers) {
  config.headers = {};
  }
  config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
  },
  (error) => {
  // do something
  return Promise.reject(error);
  }
  );

前端实现对返回未鉴权的错误,跳转登录页面

代码语言:javascript
复制
axios.interceptors.response.use(
  (response: AxiosResponse<HttpResponse>) => {
    const res = response.data;
    // if the custom code is not 20000, it is judged as an error.
    // if (res.code !== 20000) {
    if (res.code !== 0 && res.code !== 20000) {
      Message.error({
        content: res.message || 'Error',
        duration: 5 * 1000,
      });
      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (
        [50008, 50012, 50014].includes(res.code) &&
        response.config.url !== '/api/user/info'
      ) {
        Modal.error({
          title: 'Confirm logout',
          content: '您已登出,您可以取消以留在此页面,或重新登录',
          okText: '重新登陆',
          async onOk() {
            const userStore = useUserStore();

            await userStore.logout();
            window.location.reload();
          },
        });
      }
      return Promise.reject(new Error(res.message || 'Error'));
    }
    return res;
  },
  (error) => {
    Message.error({
      content: error.msg || 'Request Error',
      duration: 5 * 1000,
    });
    return Promise.reject(error);
  }
);

jwt token 解析页面:https://jwt.io/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-02-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 Jwt 和 Session 登录方案介绍
    • 登录替代方案(session)
    • 2 Jwt 实现方案
    • 3 如何实现(后端 goframe,前端 acro design in vue)
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档