深入浅出Bearer Token:解析工作原理及其在Vue、Uni-app与Java中的实现Demo

目录

  • 前言
  • 1. 基本知识
  • 2. Demo
  • 3. 实战

前言

🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF

1. 基本知识

Bearer Token是一种基于Token的认证机制,用于在HTTP请求中传递用户的身份信息

应用于RESTful API和各种Web应用中,提供了一种轻量且高效的身份验证方式

基本的作用如下:

  • 身份验证:通过Token验证用户的身份,确定其是否有权访问某个资源
  • 授权:Token中可以包含用户的权限信息,服务器可以根据Token中的信息决定用户可以进行的操作
  • 无状态认证:服务器不需要保存用户的会话状态,只需解析Token即可验证用户身份,使系统更易于扩展和管理

具体的工作原理如下:

  1. 用户登录:用户向服务器发送登录请求,提供用户名和密码等认证信息
  2. Token生成:服务器验证用户信息后,生成一个包含用户身份和权限信息的Bearer Token。 Token可以是纯字符串,也可以是经过签名的JWT(JSON Web Token)
  3. Token传输:服务器将生成的Token返回给客户端,客户端将其存储在本地(如浏览器的Local Storage或Cookie中)
  4. 请求资源:客户端在后续的HTTP请求中,将Bearer Token放在Authorization头中,发送到服务器
  5. Token验证:服务器接收到请求后,提取Bearer Token,解析其内容以验证用户身份和权限,决定是否允许请求

融入个人的一点小思考:

Uni-app部分,由于它是跨平台的框架,语法和Vue类似,我可以使用类似的逻辑来实现登录和.Token的管理。只需确保在不同设备上的本地存储方式一致即可。


在后端,使用Java开发时,我需要编写一个控制器来处理登录请求,生成Token,并将其返回给前端。同时,还需要编写过滤器或拦截器,用于检查每个请求的Authorization头,解析Token并验证。如果Token有效,则允许请求继续;否则,拒绝访问。


那么,如何辨别前端传来的Token是否有效呢?在后端,可以利用JWT的特性,比如检查Token的签名是否正确,Token是否过期,以及Token中的用户信息是否合法。也可以实现Token的黑名单机制,支持用户注销或停止某些Token的使用。


在实际项目中,还需要考虑Token的安全性,比如防止CSRF攻击、存储Token的方式(本地存储、Cookie等)、Token的有效期等。此外,还需要应对Token被泄露或被截获的风险,建议在传输中使用HTTPS协议。


经过以上的思考,我对Bearer Token有了基本的理解。它是一种基于Token的认证方式,适用于分布式系统和无状态的API设计。通过Token的生成、存储和验证,可以在没有会话管理的情况下实现用户的身份认证

~ 具体JWT的流程如下:
前端发送登录请求,包含用户名和密码
后端验证用户信息,创建Header和Payload
使用保密密钥对Header和Payload进行签名,生成完整的JWT
将JWT返回给前端,前端存储它

~ jwt验证流程:
前端在每次请求中携带JWT
后端提取JWT,验证其签名,确保未被篡改
解析Payload中的用户信息,进行权限检查

2. Demo

接下来会以Vue 以及 Uniapp对接Java的方式呈现一个Demo,主要是提供一个思路

实现Bearer Token的步骤

  1. 用户登录:客户端发送登录请求,携带用户名和密码;服务器验证用户信息,成功后生成Bearer Token;返回Token给客户端
  2. 存储Token:客户端将Token存储在安全的位置,如HTTP-only Cookie或Local Storage中;确保Token不会被恶意脚本窃取,推荐使用HTTPS传输
  3. 发送请求:客户端在后续请求中,在Authorization头添加"Bearer ";服务器接收到请求后,解析Token,验证用户身份
  4. Token验证:服务器检查Token的签名是否有效,确保Token未被篡改;验证Token是否过期,获取用户信息和权限;授权或拒绝请求

Bearer Token的过期与刷新
过期时间定义Token的存活时间,通常几分钟到几小时不等。Token过期后,用户需要重新登录以获取新的Token
刷新Token:用户在Token过期前,可以请求刷新Token,获取新的有效Token

  1. Vue实现

登录组件

<template><div class="login"><h2>登录</h2><form @submit.prevent="handleLogin"><div class="form-group"><label for="username">用户名</label><input type="text" id="username" v-model="username" required></div><div class="form-group"><label for="password">密码</label><input type="password" id="password" v-model="password" required></div><button type="submit">登录</button></form></div>
</template><script>
export default {data() {return {username: '',password: ''};},methods: {async handleLogin() {try {const response = await this.$axios.post('/api/login', {username: this.username,password: this.password});const token = response.data.token;localStorage.setItem('authToken', token);this.$router.push('/dashboard');} catch (error) {console.error('登录失败:', error);}}}
};
</script>

请求拦截器

// main.js
import Vue from 'vue';
import axios from 'axios';axios.interceptors.request.use(config => {const token = localStorage.getItem('authToken');if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;
});Vue.config.productionTip = false;
new Vue({render: h => h(App),
}).$mount('#app');
  1. Uni-app实现

登录页面

<template><view class="login"><h2>登录</h2><form @submit="handleLogin"><view class="form-group"><label>用户名</label><input type="text" v-model="username" /></view><view class="form-group"><label>密码</label><input type="password" v-model="password" /></view><button form-type="submit">登录</button></form></view>
</template><script>
export default {data() {return {username: '',password: ''};},methods: {async handleLogin() {try {const response = await this.$axios.post('/api/login', {username: this.username,password: this.password});const token = response.data.token;uni.setStorageSync('authToken', token);uni.navigateTo({ url: '/pages/dashboard/dashboard' });} catch (error) {console.error('登录失败:', error);}}}
};
</script>

Uni-app 请求拦截器

// app.vue
import Vue from 'vue';
import axios from 'axios';axios.interceptors.request.use(config => {const token = uni.getStorageSync('authToken');if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;
});Vue.config.productionTip = false;App.mpType = 'app';const app = new Vue({...App
});app.$mount();

四、后端实现:Java

User实体类

@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;@Enumerated(EnumType.STRING)private Role role;
}

Role枚举

public enum Role {USER, ADMIN
}

JwtConfig配置

@Configuration
public class JwtConfig {@Value("${ jwt.secret}")private String secret;@Value("${ jwt.expiration}")private Long expiration;@Value("${ jwt.header}")private String header;public String getSecret() {return secret;}public Long getExpiration() {return expiration;}public String getHeader() {return header;}
}

JwtUtil工具类

@Component
public class JwtUtil {@Autowiredprivate JwtConfig config;public String generateToken(User user) {Map<String, Object> claims = new HashMap<>();claims.put("id", user.getId());claims.put("username", user.getUsername());claims.put("role", user.getRole());claims.put("iat", new Date());claims.put("exp", new Date(System.currentTimeMillis() + config.getExpiration()));return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS256, config.getSecret().getBytes()).compact();}public boolean validateToken(String token) {try {Jwts.parser().setSigningKey(config.getSecret().getBytes()).parseClaimsJws(token);return true;} catch (Exception e) {return false;}}public Claims getTokenBody(String token) {return Jwts.parser().setSigningKey(config.getSecret().getBytes()).parseClaimsJws(token).getBody();}
}

LoginController

@RestController
@RequestMapping("/api")
public class LoginController {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate UserRepository userRepository;@PostMapping("/login")public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {User user = userRepository.findByUsername(loginRequest.getUsername()).orElseThrow(() -> new BadCredentialsException("用户不存在"));if (!user.getPassword().equals	loginRequest.getPassword()) {throw new BadCredentialsException("密码错误");}String token = jwtUtil.generateToken(user);return ResponseEntity.ok(new ApiResponse("登录成功", token));}
}

SecurityConfig

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/api/login").permitAll().anyRequest().authenticated().and().addFilterBefore(new JwtAuthenticationFilter(), BasicAuthenticationFilter.class);}
}

JwtAuthenticationFilter

public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtil jwtUtil;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {String header = request.getHeader("Authorization");if (header != null && header.startsWith("Bearer ")) {String token = header.substring(7);if (jwtUtil.validateToken(token)) {Claims claims = jwtUtil.getTokenBody(token);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(claims.getSubject(), null, getAuthorities((List<String>) claims.get("role")));SecurityContextHolder.getContext().setAuthentication(authentication);} else {response.setStatus(HttpServletResponse.SC_FORBIDDEN);return;}}filterChain.doFilter(request, response);}private Collection<? extends GrantedAuthority> getAuthorities(List<String> roles) {return roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}
}

3. 实战

实战中的Demo测试如下:

在这里插入图片描述

以下代码用于实战中的讲解,代码来源:https://gitee.com/zhijiantianya/ruoyi-vue-pro

uniapp中封装独特的request请求:

import store from '@/store'
import config from '@/config'
import { getAccessToken } from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { toast, showConfirm, tansParams } from '@/utils/common'let timeout = 10000
const baseUrl = config.baseUrl + config.baseApi;const request = config => {// 是否需要设置 tokenconst isToken = (config.headers || {}).isToken === falseconfig.header = config.header || {}if (getAccessToken() && !isToken) {config.header['Authorization'] = 'Bearer ' + getAccessToken()}// 设置租户 TODO 芋艿:强制 1 先config.header['tenant-id'] = '1';// get请求映射params参数if (config.params) {let url = config.url + '?' + tansParams(config.params)url = url.slice(0, -1)config.url = url}return new Promise((resolve, reject) => {uni.request({method: config.method || 'get',timeout: config.timeout ||  timeout,url: config.baseUrl || baseUrl + config.url,data: config.data,// header: config.header,header: config.header,dataType: 'json'}).then(response => {let [error, res] = responseif (error) {toast('后端接口连接异常')reject('后端接口连接异常')return}const code = res.data.code || 200const msg = errorCode[code] || res.data.msg || errorCode['default']if (code === 401) {showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录?').then(res => {if (res.confirm) {store.dispatch('LogOut').then(res => {uni.reLaunch({ url: '/pages/login' })})}})reject('无效的会话,或者会话已过期,请重新登录。')} else if (code === 500) {toast(msg)reject('500')} else if (code !== 200) {toast(msg)reject(code)}resolve(res.data)}).catch(error => {let { message } = errorif (message === 'Network Error') {message = '后端接口连接异常'} else if (message.includes('timeout')) {message = '系统接口请求超时'} else if (message.includes('Request failed with status code')) {message = '系统接口' + message.substr(message.length - 3) + '异常'}toast(message)reject(error)})})
}export default request

其中token都是存放本地:

const AccessTokenKey = 'ACCESS_TOKEN'
const RefreshTokenKey = 'REFRESH_TOKEN'// ========== Token 相关 ==========export function getAccessToken() {return uni.getStorageSync(AccessTokenKey)
}export function getRefreshToken() {return uni.getStorageSync(RefreshTokenKey)
}export function setToken(token) {uni.setStorageSync(AccessTokenKey, token.accessToken)uni.setStorageSync(RefreshTokenKey, token.refreshToken)
}export function removeToken() {uni.removeStorageSync(AccessTokenKey)uni.removeStorageSync(RefreshTokenKey)
}

后续只需要发送给后端即可

再说说前段也同理:

制作一个Jwt的格式:

import { useCache, CACHE_KEY } from '@/hooks/web/useCache'
import { TokenType } from '@/api/login/types'
import { decrypt, encrypt } from '@/utils/jsencrypt'const { wsCache } = useCache()const AccessTokenKey = 'ACCESS_TOKEN'
const RefreshTokenKey = 'REFRESH_TOKEN'// 获取token
export const getAccessToken = () => {// 此处与TokenKey相同,此写法解决初始化时Cookies中不存在TokenKey报错return wsCache.get(AccessTokenKey) ? wsCache.get(AccessTokenKey) : wsCache.get('ACCESS_TOKEN')
}// 刷新token
export const getRefreshToken = () => {return wsCache.get(RefreshTokenKey)
}// 设置token
export const setToken = (token: TokenType) => {wsCache.set(RefreshTokenKey, token.refreshToken)wsCache.set(AccessTokenKey, token.accessToken)
}// 删除token
export const removeToken = () => {wsCache.delete(AccessTokenKey)wsCache.delete(RefreshTokenKey)
}/** 格式化token(jwt格式) */
export const formatToken = (token: string): string => {return 'Bearer ' + token
}

后端Java的token只需校验即可:

在这里插入图片描述

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

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

相关文章

kubernetes——part3-5 核心概念 Service

一、 service作用 使用kubernetes集群运行工作负载时&#xff0c;由于Pod经常处于用后即焚状态&#xff0c;Pod经常被重新生成&#xff0c;因此Pod对应的IP地址也会经常变化&#xff0c;导致无法直接访问Pod提供的服务&#xff0c;Kubernetes中使用了Service来解决这一问题&am…

从零开始 | C语言基础刷题DAY1

❤个人主页&#xff1a;折枝寄北的博客 DAY1[2025.3.11] 1. 求两个数的较大值2.从键盘输入的两个数的大小关系3.一个整数的奇偶性&#xff0c;请判断4. 考试分数是否通过5.考试成绩是否完美&#xff0c;请判断 1. 求两个数的较大值 题目&#xff1a; 写一个函数求两个整数的较…

开源模型时代的 AI 开发革命:Dify 技术深度解析

开源模型时代的AI开发革命&#xff1a;Dify技术深度解析 引言&#xff1a;AI开发的开源新纪元 在生成式AI技术突飞猛进的2025年&#xff0c;开源模型正成为推动行业创新的核心力量。据统计&#xff0c;全球超过80%的AI开发者正在使用开源模型构建应用&#xff0c;这一趋势不仅…

Dify Web 前端独立部署指南(与后端分离,独立部署)

背景:单独拆分前端出来部署,二开前后端 本文档专注于 Dify Web 前端的部署流程和配置,适用于需要将项目部署到各种环境的运维人员和开发者。 1. 环境准备 1.1 部署环境要求 Node.js >= 18.17.0Nginx 或其他Web服务器(生产环境推荐)Docker(可选,用于容器化部署)1.…

《苍穹外卖》SpringBoot后端开发项目核心知识点整理(DAY1 to DAY3)

目录 一、在本地部署并启动Nginx服务1. 解压Nginx压缩包2. 启动Nginx服务3. 验证Nginx是否启动成功&#xff1a; 二、导入接口文档1. 黑马程序员提供的YApi平台2. YApi Pro平台3. 推荐工具&#xff1a;Apifox 三、Swagger1. 常用注解1.1 Api与ApiModel1.2 ApiModelProperty与Ap…

大数据hadoop课程笔记

1.课程导入 柯洁 Alpha Go是人工智能领域的里程碑。 深度学习 大模型deepseek chatgpt 大模型 和 大数据 之间有着非常紧密的关系。可以说&#xff0c;大数据是大模型发展的基石&#xff0c;而大模型是大数据价值挖掘的重要工具。 https://youtu.be/nN-VacxHUH8?sifj7Ltk…

架构学习第八周--Kubernetes博客搭建

目录 一、整体架构 二、部署MySQL主从 三、部署Redis哨兵 四、部署WordPress 五、注意事项 一、整体架构 本项目为在一主三从的Kubernetes集群上部署WordPress博客。因为WordPress部分容器版本自行集成Apache和PHP服务&#xff0c;因此在Kubernetes上部署WordPress只需提供…

Application.OnTime如何引用带参数的过程

Application.OnTime方法本身并不直接支持传递参数给被调用的过程。不过&#xff0c;有几种方法可以间接实现这个需求。 方法1&#xff1a;使用单引号表达式 使用单引号表达式来传递参数时&#xff0c;不能在表达式中使用变量&#xff0c;需要把参数值直接写到表达中&am…

网络安全之tcpdump工具

引言 wireshark是一款非常不错的抓包软件&#xff0c;在图形化界面占绝对统治地位&#xff1b;尽管其在字符界面下有些许选项可供使用&#xff0c;但终究不太方便&#xff0c;下面我再介绍一款NB的终端抓包工具 tcpdump 1、混杂模式 linux的网卡有混杂模式一说&#xff0c;当开…

VC++ 获取目的IP的路由

GetBestRoute 函数获取到目的IP的最佳匹配路由。 第一个参数为&#xff1a;destination&#xff08;目的IP&#xff09; 第二个参数为&#xff1a;source&#xff08;源IP&#xff09; 通常不需要指定第二个source&#xff0c;这个一般用来匹配具体某一个网卡接口路由的&…

JavaScript 模块 vs C# 类:封装逻辑的两种哲学

引言 在现代软件开发中&#xff0c;模块化和面向对象设计是代码组织的核心课题。本文通过对比 JavaScript 模块&#xff08;ES6 Module&#xff09;与 C# 类&#xff08;Class&#xff09;的实现方式&#xff0c;探讨两种语言在封装逻辑时的不同哲学&#xff0c;并给出实际应用…

大模型在甲状腺癌诊疗全流程预测及方案制定中的应用研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 1.3 国内外研究现状 二、大模型预测甲状腺癌的理论基础 2.1 甲状腺癌相关医学知识 2.2 大模型技术原理与特点 2.3 大模型在医疗领域的应用潜力 三、术前预测方案 3.1 预测模型构建 3.1.1 数据收集与预处理 …

electron+vue+webview内嵌网页并注入js

vue内嵌网页可以使用iframe实现内嵌网页&#xff0c;但是只能通过postMessage间接通信&#xff0c;在electron环境下&#xff0c;vue可以直接使用webview来内嵌网页&#xff0c;支持 executeJavaScript、postMessage、send 等丰富的通信机制。 使用 webview的优势 性能更佳&…

leetcode日记(95)将有序数组转换为二叉搜索树

很简单&#xff0c;感觉自己越来越适应数据结构题目了…… /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : va…

【threejs实战教程一】初识Three.js,场景Scene、相机Camera、渲染器Renderer

Three.js是一个基于WebGL的JavaScript 3D图形库&#xff0c;用于在浏览器中创建和显示3D内容 Three.js中最基础的三个关键要素就是场景Scene、相机Camera、渲染器Renderer 通俗一点理解&#xff0c;场景就是我们生活中一个具体的场景&#xff0c;比如自然环境中的一栋建筑&…

【leetcode hot 100 138】随机链表的复制

解决一&#xff1a;回溯 哈希表 本题要求我们对一个特殊的链表进行深拷贝。如果是普通链表&#xff0c;我们可以直接按照遍历的顺序创建链表节点。而本题中因为随机指针的存在&#xff0c;当我们拷贝节点时&#xff0c;「当前节点的随机指针指向的节点」可能还没创建&#xf…

木马查杀之AST初识篇

一、AST 定义 抽象语法树&#xff08;Abstract Syntax Tree&#xff0c;AST&#xff09;是源代码的一种抽象表示形式。它以树状结构描述源代码的语法构成&#xff0c;树上的每个节点都对应源代码中的一个语法结构或元素&#xff0c;像变量声明、函数调用、表达式等。通过这种结…

vscode接入DeepSeek 免费送2000 万 Tokens 解决DeepSeek无法充值问题

1. 在vscode中安装插件 Cline 2.打开硅基流动官网 3. 注册并登陆&#xff0c;邀请码 WpcqcXMs 4.登录后新建秘钥 5. 在vscode中配置cline (1) API Provider 选择 OpenAI Compatible &#xff1b; (2) Base URL设置为 https://api.siliconflow.cn](https://api.siliconfl…

如何在保持安全/合规的同时更快地构建应用程序:DevOps 指南

随着敏捷思维方式的兴起&#xff0c;开发和 DevOps 团队都面临着持续的压力&#xff0c;他们需要以迭代方式缩短发布周期并加快部署速度&#xff0c;以满足不断增长的客户期望。随着这种对速度的追求越来越强烈&#xff0c;维护安全性和合规性标准的复杂性也随之增加。 当今 D…

Java中常见的PO、VO、DAO、BO、DO、DTO、POJO、Query类解释(通俗易懂)

文章目录 先点击收藏和点赞,切勿白嫖,感谢一丶PO(persistant object)持久对象二丶VO(value object)值对象三丶DAO(Data Access Objects) 数据访问对象接口四丶BO/DO(Business Object) 业务对象层五丶DTO(Data Transfer Object) 数据传输对象六丶POJO(Plain Old Java Objects) 简…