java实现后端身份验证的Token令牌

写在前面的话:
蒽,Token是一个什么东西?那我们为什么要用它?

Token:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端的请求只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。

同时在分布式中我们会选用Token来身份验证,而不是session。 原因就是:
session是有状态的,一般存于服务器内存中,当服务器采用分布式时,session就会面对负载均衡问题如果遇到负载均衡多服务器的情况,那我们就不好确认当前用户是否登录,因为多服务器不共享session。但是token是无状态的,token字符串里就保存了所有的用户关键信息

看一个简单的Token结构:

简单说一下,Token的原理:

1、上面图片中的Token,前面一串"id:1001,status:1,endtime:2022-01-08 19:18:43"字符串就是服务端在用户第一次登录的时候前端传入的用户名及密码验证成功,后端就查询该用户的id以及状态,即该账号是否正常使用中,或是已经停用的账号,endtime是后端产生Token的当前时间加一个有效时间,作为验证Token的有效时间。后面的fb675266364f697519dac6d1e6ec1da3 是根据前面的字符串生成的MD5加密码。
2、ImlkOjEwMDIzLHN0YXR1czowLGVuZHRpbWU6MjAyMi0wMS0wOSAyMDozNTo0OCI7MjEwOTJkNDRkNzVjOTM2NzY0YThiODA3ZmEzYWU1NzE=
这一串是后端返回给前端看到的数据,当然就是把"id:1001,status:1,endtime:2022-01-08 19:18:43";fb675266364f697519dac6d1e6ec1da3进行base64编码,对数据进行一定保护。
3、前端收到后端的登录成功消息后,同时保存Token,以后的每次请求都要带上Token(放在请求头)。
4、后端接收到前端的业务请求后,首先验证Token是否有效,即Token中的数据有没有更改(切确的说是看有没有人伪造用户来后端请求,冒充用户)。
5、这里各位看友可能会有点迷糊,请看下面简单的交互图


现在开始代码阶段:

1、先数据库连接成功
2、接受前端请求,后端查询数据库判断是否创建Token,即该用户是否合法
3、合法验证成功,即开始创建Token并传给前端
4、前端拿到Token并保存,当前登录成功
5、前端再次发送请求给后端,带上Token(放在请求头内)
6、后端接受请求,首先验证Token是否有效,若有效则返回前端的业务请求,并将当前新的Token传给前端
7、前端保存新的Token,为后面的请求,即完成一个Token的流程

1、数据库的连接如下:

 private static final java.lang.String url = "jdbc:mysql://localhost:3306/xiaofang?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";private static final java.lang.String userName = "root";private static final java.lang.String passwd = "root";static {try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}**数据库文件:**```sql
/*Navicat Premium Data TransferSource Server         : mysqlSource Server Type    : MySQLSource Server Version : 80025Source Host           : localhost:3306Source Schema         : xiaofangTarget Server Type    : MySQLTarget Server Version : 80025File Encoding         : 65001Date: 09/01/2022 22:23:32
*/SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`head` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`addr` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`status` int(0) NULL DEFAULT NULL,`created` datetime(6) NULL DEFAULT NULL,`deleted` datetime(6) NULL DEFAULT NULL,`type` int(0) NOT NULL,PRIMARY KEY (`username`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1001', 'cc', 'cc', 'cc123', NULL, '/static/img/user.jpg', NULL, 1, '2022-01-08 19:00:00.000000', NULL, 0);
INSERT INTO `user` VALUES ('10023', 'tt', 'tt', 'tt456', NULL, NULL, NULL, NULL, NULL, NULL, 0);SET FOREIGN_KEY_CHECKS = 1;

2、同样的前端我们不写页面,后端直接用Servlet的doget拿参数,来验证:
不清楚Servlet前后端交互的看友,可以看看我前面的这篇文章Servlet前后端简单交互

     @Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");String name = req.getParameter("name");String password = req.getParameter("password");try (Connection conn = DriverManager.getConnection(url, userName, passwd);PreparedStatement ps = conn.prepareStatement("select * from user where name =? and password =?;")) {ps.setString(1, name);ps.setString(2, password);ResultSet rs = ps.executeQuery();//字符串等于比较,要记得不要混了用equalswhile (rs.next()) {if ((rs.getString("name").equals(name)) && (rs.getString("password").equals(password))) {resp.getWriter().println("登录成功!欢迎您" + rs.getString("name"));int id = rs.getInt("id");int status = rs.getInt("status");resp.getWriter().println("Token的值为:" + createToken(id, status));} else {resp.getWriter().println("-----登录失败!-----");resp.getWriter().println("-----用户名或密码错误!-----");}}rs.close();} catch (SQLException throwables) {throwables.printStackTrace();}}

3、合法验证成功,即开始创建Token并传给前端

/*** 创建Token对象** @param id* @param status*/public static String createToken(int id, int status) throws UnsupportedEncodingException {SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = new Date();//当前时间加五分钟,作为Token的有效时间Date dateafter = new Date(date.getTime() + 300000);Object endtime = sif.format(dateafter);String json = "id:" + id + ",status:" + status + ",endtime:" + endtime;JSONObject jsonObject = new JSONObject();String ss = jsonObject.toJSONString(json);String begin = ss + ";" + getMd5(ss);//调用并转换成base64//这里的Base64码转换idea提供了相关的方法,我是调用了自己写的Base64码,想看的可以关注我,看我的文章哦return Base644.Encoding(begin);}

将Token传给前端的代码,写在了登录验证成功就创建并传递给前端

  resp.getWriter().println("Token的值为:" + createToken(id, status));


4、前端再次发送请求给后端,带上Token(放在请求头内)
这里有多种请求方法,比如Postman,或者Idea的.http请求文件


Token.http内容:
要知道这个是前端的请求,要带上我们前面后端给的Token

POST http://localhost:8080/war/helloworld
Content-Type: application/json;charset=utf-8
Token: ImlkOjEwMDEsc3RhdHVzOjEsZW5kdGltZToyMDIyLTAxLTA5IDIwOjE4OjAwIjszZjRmYzMyMWE3Y2FlNThmMjhmNzBkYmYxNTg2YmNkMg=={"id": 1001,"name": "云先生","date": [{"create_time": "2022-01-06"}]
}


5、后端接受请求,首先验证Token是否有效,若有效则返回前端的业务请求,并将当前新的Token传给前端。

@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");try {timethan(req, resp);//如果用户身份的token验证通过,进行前端请求的业务操作,// 并返回前端一个新的token} catch (ParseException e) {e.printStackTrace();}}/*** 后端拿到前端请求头的token对象,验证身份,比较有效时间** @param req* @param resp* @return* @throws ParseException*/public static void timethan(HttpServletRequest req, HttpServletResponse resp) throws ParseException, IOException {//拿到token对象String str = Base644.Decoding(req.getHeader("Token"));//将token切割成两部分//str:"id:1001,status:1,endtime:2022-01-08 19:18:43";fb675266364f697519dac6d1e6ec1da3String ahead = str.substring(0, str.indexOf(";"));String behind = str.substring(str.indexOf(";") + 1, str.length());//时间从字符串中截取出来String ss = ahead.substring(ahead.indexOf("e"), ahead.length());String cc = ss.substring(ss.indexOf(":") + 1, ss.length());//将字符串的时间转为long类型的数据进行比较SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date data = sif.parse(cc);long stime = data.getTime();Date date1 = new Date();Date dateafter = new Date(date1.getTime());long nowtime = dateafter.getTime();long differen = stime - nowtime;//解析当前token对象的id,状态int id = Integer.parseInt(ahead.substring(ahead.indexOf("i") + 3, ahead.indexOf(",")));int status = Integer.parseInt(ahead.substring(ahead.indexOf("s") + 7, ahead.lastIndexOf(",")));if (getMd5(ahead).equals(behind)) {try {if (differen < 0) {resp.getWriter().println("Token有效时间已过,请重新登录!");} else {resp.getWriter().println("前端请求的业务处理结果,后端已送到,请接受!");resp.getWriter().println("Token:"+createToken(id,status));}} catch (IOException e) {e.printStackTrace();}} else {resp.getWriter().println("Token信息有误!");}}

最后,前端继续把Token保存,留作下一次的使用。 到了这里Token一个的前后端交互就完成了。

全部源码:

package com.example.demo.newyears;import com.alibaba.fastjson.JSONObject;import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.*;// 先数据库连接成功
// 接受前端请求,后端查询数据库判断是否创建Token,即该用户是否合法
// 合法验证成功,即开始创建Token并传给前端
// 前端拿到Token并保存,当前登录成功
// 前端再次发送请求给后端,带上Token(放在请求头内)
// 后端接受请求,首先验证Token是否有效,若有效则返回前端的业务请求,并将当前新的Token传给前端
// 前端保存新的Token,为后面的请求,即完成一个Token的流程/*** @Author:Yun* @Date:2022/01/08/11:50* @Description:**/
@WebServlet(name = "test", urlPatterns = "/helloworld")
public class Token extends HttpServlet {private static final java.lang.String url = "jdbc:mysql://localhost:3306/xiaofang?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";private static final java.lang.String userName = "root";private static final java.lang.String passwd = "root";static {try {Class.forName("com.mysql.cj.jdbc.Driver");} catch (ClassNotFoundException e) {e.printStackTrace();}}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");String name = req.getParameter("name");String password = req.getParameter("password");try (Connection conn = DriverManager.getConnection(url, userName, passwd);PreparedStatement ps = conn.prepareStatement("select * from user where name =? and password =?;")) {ps.setString(1, name);ps.setString(2, password);ResultSet rs = ps.executeQuery();//字符串等于比较,要记得不要混了用equalswhile (rs.next()) {if ((rs.getString("name").equals(name)) && (rs.getString("password").equals(password))) {resp.getWriter().println("登录成功!欢迎您" + rs.getString("name"));int id = rs.getInt("id");int status = rs.getInt("status");resp.getWriter().println("Token的值为:" + createToken(id, status));} else {resp.getWriter().println("-----登录失败!-----");resp.getWriter().println("-----用户名或密码错误!-----");}}rs.close();} catch (SQLException throwables) {throwables.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setHeader("Content-Type", "application/json;charset=UTF-8");try {timethan(req, resp);//如果用户身份的token验证通过,进行前端请求的业务操作,// 并返回前端一个新的token} catch (ParseException e) {e.printStackTrace();}}/*** 后端拿到前端请求头的token对象,验证身份,比较有效时间** @param req* @param resp* @return* @throws ParseException*/public static void timethan(HttpServletRequest req, HttpServletResponse resp) throws ParseException, IOException {//拿到token对象String str = Base644.Decoding(req.getHeader("Token"));//将token切割成两部分//str:"id:1001,status:1,endtime:2022-01-08 19:18:43";fb675266364f697519dac6d1e6ec1da3String ahead = str.substring(0, str.indexOf(";"));String behind = str.substring(str.indexOf(";") + 1, str.length());//时间从字符串中截取出来String ss = ahead.substring(ahead.indexOf("e"), ahead.length());String cc = ss.substring(ss.indexOf(":") + 1, ss.length());//将字符串的时间转为long类型的数据进行比较SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date data = sif.parse(cc);long stime = data.getTime();Date date1 = new Date();Date dateafter = new Date(date1.getTime());long nowtime = dateafter.getTime();long differen = stime - nowtime;//解析当前token对象的id,状态int id = Integer.parseInt(ahead.substring(ahead.indexOf("i") + 3, ahead.indexOf(",")));int status = Integer.parseInt(ahead.substring(ahead.indexOf("s") + 7, ahead.lastIndexOf(",")));if (getMd5(ahead).equals(behind)) {try {if (differen < 0) {resp.getWriter().println("Token有效时间已过,请重新登录!");} else {resp.getWriter().println("前端请求的业务处理结果,后端已送到,请接受!");//创建一个新的Tokenresp.getWriter().println("Token:"+createToken(id,status));}} catch (IOException e) {e.printStackTrace();}} else {resp.getWriter().println("Token信息有误!");}}/*** 创建Token对象** @param id* @param status*/public static String createToken(int id, int status) throws UnsupportedEncodingException {SimpleDateFormat sif = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = new Date();Date dateafter = new Date(date.getTime() + 300000);Object endtime = sif.format(dateafter);String json = "id:" + id + ",status:" + status + ",endtime:" + endtime;JSONObject jsonObject = new JSONObject();String ss = jsonObject.toJSONString(json);System.out.println(ss);String begin = ss + ";" + getMd5(ss);//调用并转换成base64return Base644.Encoding(begin);}/*** 将字符串转为MD5加密的方法** @param org* @return*/public static String getMd5(String org) {MessageDigest md = null;try {md = MessageDigest.getInstance("MD5");md.reset();md.update(org.getBytes(StandardCharsets.UTF_8));byte[] byteArray = md.digest();StringBuilder md5StrBuff = new StringBuilder();for (int i = 0; i < byteArray.length; i++) {if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));elsemd5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));}return md5StrBuff.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}
}

最后有问题的看友,可以留言或者私聊我欧。

java实现后端身份验证的Token令牌相关推荐

  1. vue+node.js前后端交互中的token令牌验证

    这篇文章分享一下本人学习vue+node.js前后端交互中的登录token令牌的心得 最近准备写一个个人博客网站,前端采用的是vue+element,后端用node.js 在做用户登录的时候就想到 如 ...

  2. 两步验证杀手锏:Java 接入 Google 身份验证器实战

    转载自   两步验证杀手锏:Java 接入 Google 身份验证器实战 什么是两步验证? 大家应该对两步验证都熟悉吧?如苹果有自带的两步验证策略,防止用户账号密码被盗而锁定手机进行敲诈,这种例子屡见 ...

  3. asp.net Web API 身份验证 不记名令牌验证 Bearer Token Authentication 简单实现

    1. Startup.Auth.cs文件 添加属性 1 public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; ...

  4. flask身份验证_Flask基于令牌的身份验证

    flask身份验证 This tutorial takes a test-first approach to implementing token-based authentication in a ...

  5. Java实现谷歌身份验证器

    生成一个随机秘钥 public static String generateSecretKey() throws NoSuchAlgorithmException {SecureRandom sr = ...

  6. Java使用google身份验证器实现动态口令验证

    google身份认证器服务端key的生成和它生成的随机密码的验证: 客户端和服务器事先协商好一个密钥K,用于一次性密码的生成过程,此密钥不被任何第三方所知道.此外,客户端和服务器各有一个计数器C,并且 ...

  7. Spring Security:身份验证令牌Authentication介绍与Debug分析

    在Spring Security中,通过Authentication来封装用户的验证请求信息,Authentication可以是需要验证和已验证的用户请求信息封装.接下来,博主介绍Authentica ...

  8. 数字化时代,基于令牌的身份验证是如何工作?

    一.背景 数字化转型给用户带来了安全问题,以保护他们的身份免受假冒.据美国诺顿称,平均每年有 80 万个帐户被黑客入侵.需要用于身份验证的高安全性系统和网络安全法规. 传统方法依赖于使用用户名和密码的 ...

  9. weblogic登录验证被拒绝_使用Kubernetes身份在微服务之间进行身份验证

    如果您的基础架构由相互交互的多个应用程序组成,则您可能会遇到保护服务之间的通信安全以防止未经身份验证的请求的问题. 想象一下,有两个应用程序: API datastore 您可能希望数datastor ...

最新文章

  1. mkfs.ext4 /dev/sdb 与 mkfs.ext4 /dev/sdb1
  2. Docker背后的内核知识:命名空间资源隔离
  3. Redis附加功能之键过期功能
  4. matlab保存图片如何保证dpi,[转载]matlab 保存高分辨率图像 dpi要在300以上
  5. vs2010添加TSTCON( ActiveX Control Test Container )工具
  6. idea调试debug技巧_这几个IDEA高级调试技巧,用了都说好!
  7. C#的foreach
  8. web.xml 基本配置
  9. python format()函数的用法
  10. ssh secure shell client 保存密码_著名的SSH协议
  11. chrome安装JSONview插件,即可在浏览中查看json文件
  12. 无线射频识别技术开发与应用学习视频
  13. pdfpcell输出换行_fpdf 的cell 中文自动换行问题
  14. 安装kali步骤(超级超级详细)
  15. excel表格怎么调整行高和列宽_同事看呆!文档怎么打印在一页上?这招超简单!...
  16. wordpress博客添加微博、微信分享等等
  17. java中的参数传递(只有值传递没有引用传递)
  18. 原生js实现字幕滚动
  19. Python —— Numpy数组组合
  20. 网易月薪25K的Linux运维面试真题曝光

热门文章

  1. 现金红包 java开发_微信现金红包开发1(JAVA)
  2. 【设计模式】UML类图关系与符号
  3. 针对淘客链接的优化处理方案
  4. JDBC编程六步中遇到的问题
  5. 《OPPO安全——安全与隐私小课堂》之 日防夜防,“隐私窃贼“难防?手把手教你如何使用OPPO手机管理应用权限
  6. linux下安装.hex文件,HexEdit Linux下命令集
  7. 本科毕业--别恩师李永飞
  8. 解决Oracle的CLOB数据类型大于4000时的数据太大的问题ORA-01704: string literal too long
  9. Gnocchi: 3、Gnocchi命令行使用
  10. 【SQL高级教程】数据库学习笔记