[golang] 实现 jwt 方式登录

news2024/12/26 11:00:31

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)

登录实现

// 登录
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
}

鉴权实现

// 检测登录 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
}

后台签证续期逻辑

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 和续期操作

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);
  }
  );

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

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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/342248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Spark分布式内存计算框架——Spark Core】11. Spark 内核调度(下)

8.5 Spark 基本概念 Spark Application运行时&#xff0c;涵盖很多概念&#xff0c;主要如下表格&#xff1a; 官方文档&#xff1a;http://spark.apache.org/docs/2.4.5/cluster-overview.html#glossary Application&#xff1a;指的是用户编写的Spark应用程序/代码&#x…

leetcode练习二:排序

文章目录排序一、排序算法1.1 冒泡排序1.1.1 算法步骤1.1.2 算法分析1.1.3 代码实现&#xff1a;1.1.4 冒泡排序优化1.2 选择排序1.2.1 算法步骤1.2.2 算法分析1.2.3 代码实现1.3 插入排序1.3.1 算法步骤1.3.2 算法分析1.3.3 代码实现1.4 希尔排序1.4.1 算法步骤1.4.2 算法分析…

【网络基础】DNS是什么

本文不会直接引入复杂枯燥概念&#xff0c;用形象例子通俗讲解&#xff0c;旨在入门理解。 DNS作用 DNS是用来做域名解析的。 相当于把网址翻译成实际ip地址&#xff0c;供其他设备访问。 一个例子 有一个网站的服务器IP地址为1.1.1.1&#xff0c;用电脑访问该网站的话只需…

挂载Samba到Windows系统和Linux系统

1、搭建Samba服务 1.1安装Samba服务 yum -y install samba结果如下&#xff1a; 1.2配置Samba服务 修改Samba服务的配置文件 vim /etc/samba/smb.conf[sambadir]自定义路径 comment Samba Directories自定义描述 path /samba/dir自定义路径 [global]workgroup SAMBAsec…

ElasticSearch-学习笔记-阶段总结(易错点易混淆点归纳)

Java后端-学习路线-笔记汇总表【黑马程序员】ElasticSearch-学习笔记01【ElasticSearch基本介绍】【day01】ElasticSearch-学习笔记02【ElasticSearch索引库维护】ElasticSearch-学习笔记03【ElasticSearch集群】ElasticSearch-学习笔记04【Java客户端操作索引库】【day02】Ela…

基于python shapely的几何集合操作

前言shapely是基于笛卡尔坐标的几何对象操作和分析Python库。底层基于GEOS和JTS库。shapely无法读取和写数据文件&#xff0c;但可以基于应用广泛的一些格式和协议进行序列化(serialize)和去序列化(deserialize)操作。而且shapely不关注数据格式和坐标系统&#xff0c;但shapel…

05 react img css修改svg图片样式

react img css修改svg图片样式svg图片的相关理论定义优点前端引入svg图片的方式方式一&#xff1a;<svg>标签引入&#xff0c;内嵌到 HTML 中方式二&#xff0c;修改svg的颜色方式三&#xff1a;<img>标签引入1、元素模糊2、元素亮度3、元素投影4、元素的灰色程度5…

乐鑫特权隔离机制的 OTA 固件升级

固件空中升级 (OTA, Over-The-Air) 是任何联网设备的重要功能之一&#xff0c;支持开发人员通过远程更新固件&#xff0c;以发布新功能或修复错误。乐鑫特权隔离框架中包含两类应用程序&#xff1a;受保护的应用程序 (protected_app) 和用户应用程序 (user_app) &#xff0c;这…

互联网舆情监测系统的发展阶段,TOOM互联网舆情监测系统有哪些?

互联网舆情监测系统是一种利用计算机技术对互联网上的大量信息进行实时监测、分析和评估的工具&#xff0c;旨在了解公众对某一事件、话题或品牌等的态度、情感倾向和影响力等。通过对社交媒体、论坛、新闻媒体等多个渠道的数据采集和处理&#xff0c;系统能够实现舆情事件的追…

【学习总结】IMU预积分推导

本文仅用于记录自己学习总结。记录IMU预积分推导过程&#xff0c;不包含具体原理。 符号表示 RRR: 表示旋转矩阵 vvv: 表示速度 ppp: 表示位移 ExpExpExp: 指数映射&#xff0c;将旋转向量映射为旋转矩阵 w~\widetilde{w}w: 角速度观测值 f~\widetilde{f}f​: 加速度观测值 bg…

【Hello Linux】Linux工具介绍 (yum vim)

作者&#xff1a;小萌新 专栏&#xff1a;Linux 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;介绍Linux的常用工具 yum和vim Linux工具介绍Linux中的软件管理工具 -- yum在windows下安装软件的方式在Linux下安装软件的方式认识yum…

安警官的IP地址是怎样定位到莽村附近的?

要说最近大火的电视剧非《狂飙》莫属。电视剧《狂飙》自开播以来&#xff0c;一举超过《三体》《去有风的地方》等先播电视剧&#xff0c;收视率一路“狂飙”&#xff0c;牢牢占据近期的收视冠军。 在剧中&#xff0c;张译扮演一名坚持公平、正义与理想的人民警察“安欣”&…

20230210组会总结

文章目录Ultra-High-Definition Low-Light Image Enhancement: A Benchmark and Transformer-Based MethodShuffleMixer: An Efficient ConvNet for Image Super-ResolutionA Close Look at Spatial Modeling: From Attention to ConvolutionDEA-Net: Single image dehazing ba…

关于国外服务器租用疑问大全解答

很多人对于租用国外服务器还是有些疑虑的&#xff0c;比如是否违法、价格、是否需要备案等等一系列问题&#xff0c;直接导致了很多有疑虑的用户放弃了使用&#xff0c;那么下面我们就来解答关于这些租用国外服务器的疑问&#xff0c;打消用户的疑虑。 1、租用国外服务器违法吗…

量化选股——基于动量因子的行业风格轮动策略(第1部分—因子测算)

文章目录动量因子与行业轮动概述动量因子的理解投资视角下的行业轮动现象投资者视角与奈特不确定性动量因子在行业风格上的效果测算动量因子效果测算流程概述1. 行业选择&#xff1a;申万一级行业2. 动量因子选择&#xff1a;阿隆指标&#xff08;Aroon&#xff09;3. 测算方法…

java中字符串首字母变大写的两种方法

public class 快速排序 {public static void main(String[] args) {int[] arr new int[]{5, 2, 9, 6, 22, 21};//System.out.println(Arrays.toString(kuaiPai(arr)));// System.out.println(Arrays.asList("dada", "dda", "ddd"));//System.o…

VR全景带你浪漫“狂飙”情人节,见证甜蜜心动

当情人节遇上VR&#xff0c;足以让情侣过一个难忘的情人节。马上情人节就要到了&#xff0c;大家是不是还在绞尽脑汁的想着&#xff0c;如何和另一半过一个浪漫的情人节呢&#xff1f;老套的剧情已经不能吸引人了&#xff0c;让我们看看VR全景给情人节带来了哪些不同的体验吧&a…

喜欢大屏电视?那就选择酷开系统,实现智能生活享受

随着科技的发展和我们生活水平的提高&#xff0c;越来越多的消费者开始认可并习惯使用各种高质量的科技产品&#xff0c;比如喜欢玩游戏的消费者&#xff0c;他们往往会追求流畅性更强、刷新率更快的大显示屏&#xff0c;以此获得更真实刺激的游戏体验&#xff0c;而喜欢追剧的…

面对“开门红”,跨境支付如何寻求新增长曲线?

易观&#xff1a;2022年是第三方支付行业洗牌加剧的一年&#xff0c;在部分机构选择退出的过程中&#xff0c;也有机构开始瞄准跨境业务&#xff0c;成为了支付机构转型的重要方向之一。跨境支付是指两个或及其以上的国家或地区进行国际贸易、国际投资或其他经济活动&#xff0…

基于乐鑫 ESP32 的智能手表

拥有一块自己制作的智能手表是不是听起来就很酷&#xff1f;工程师 Pcadic 基于乐鑫 ESP32-PICO-D4 实现了这个想法&#xff01;他开发了一款能够与我们的手机或任何其他设备互联的智能手表 ESP32-Pico-Watch。 ESP32-Pico-Watch 支持 Wi-Fi、蓝牙、Mesh&#xff0c;拥有 1 个振…