当前位置:   article > 正文

用户登陆实现前后端JWT鉴权_jwt鉴权登录

jwt鉴权登录

目录

一、JWT介绍

二、前端配置

三、后端配置

四、实战


一、JWT介绍

1.1 什么是jwt

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间以安全的方式传输信息。JWT 是一种紧凑、自包含的信息载体,可以被解码和验证。它通常用于身份验证和授权服务,特别是在无状态的 Web 应用程序中,比如那些基于 REST 的 API。

1.2 jwt的结构

JWT 由三部分组成,每一部分都由点号(.)分隔开:

  1. 头部 (Header): 包含关于类型和签名算法的信息。例如:

{"alg":"HS256","typ":"JWT"}

这个头部通常表明使用 HMAC SHA-256 算法签名。

  • 负载 (Payload): 也称为“声明”(Claims),包含了要传输的信息。这些信息可以是任意的 JSON 数据,但通常包括一些标准的字段,例如:

{"sub":"1234567890","name":"John Doe","admin":true}

这里 "sub" 是主题(Subject),"name" 是姓名,"admin" 是权限声明。

  • 签名 (Signature): 用于验证数据的完整性和确认发送者的身份。签名是通过一个密钥对头部和负载进行加密得到的。

  1. HMACSHA256(
  2. base64UrlEncode(header) + "." +
  3. base64UrlEncode(payload),
  4. secret
  5. )

如果使用非对称加密,则密钥可以是公钥或私钥。

1.3 jwt工作流程

  1. 创建和签发: 服务器创建一个 JWT,其中包含用户的身份信息和/或其他数据,然后使用一个秘密密钥或私钥对其进行签名。

  2. 传输: JWT 通过网络发送给客户端,通常作为 HTTP Authorization header 的一部分。

  3. 验证和使用: 当客户端向服务器发送请求时,它将 JWT 作为身份验证的一部分。服务器验证 JWT 的签名,以确保它没有被篡改,并从中读取信息。

  4. 过期: JWT 可以设置一个过期时间,在此之后,它将不再有效。

下面将通过Vue + SpringBoot 实现一个jwt鉴权的项目


二、前端配置

2.1 引入axios

npm install axios

通过添加前端拦截器配置axios

在src下创建一个utils包,再创建一个axios.js文件

  1. import axios from 'axios';
  2. // 创建axios实例
  3. const instance = axios.create();
  4. // 添加请求拦截器
  5. instance.interceptors.request.use(
  6. function (config) {
  7. // 在这里添加token到请求头
  8. const token = localStorage.getItem('token') || ''; // 从本地存储获取token
  9. if (token) {
  10. config.headers.Authorization = `${token}`;
  11. }
  12. return config;
  13. },
  14. function (error) {
  15. // 请求错误时的处理
  16. return Promise.reject(error);
  17. }
  18. );
  19. export default instance;

 在main.js中配置应用axios

  1. import axios from './utils/axios';
  2. Vue.prototype.$axios = axios;

 

2.3 使用axios

在配置全局后,使用axios就并不需要单独引入axios了,直接使用this.$axios即可调用

  1. this.$axios.get('/api/forum/getAllForumPost', {
  2. params: {
  3. pageSize: 1,
  4. pageNumber: 10
  5. }
  6. }).then((response) => {
  7. console.log(response.data.data);
  8. this.posts = response.data.data;
  9. });

创建一个TestView.vue测试发送请求时候是否会携带请求头

  1. <template>
  2. <div>
  3. <!-- 测试是否会携带请求头 -->
  4. <button @click="Test"> 发送测试</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. data() {
  10. return {
  11. };
  12. },
  13. methods: {
  14. Test(){
  15. // 假设有登录成功后的token
  16. localStorage.setItem('token', '1234567890');
  17. this.$axios.get('/api/Test').then((response) => {
  18. console.log(response.data.data);
  19. });
  20. }
  21. },
  22. };
  23. </script>

在控制台的网络中查看是否有对应的请求头

已经成功携带,并且名称为Authorization


三、后端配置

3.1 引入依赖

  1. <!-- JWT依赖-->
  2. <dependency>
  3. <groupId>io.jsonwebtoken</groupId>
  4. <artifactId>jjwt</artifactId>
  5. <version>0.9.1</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>javax.xml.bind</groupId>
  9. <artifactId>jaxb-api</artifactId>
  10. <version>2.3.0</version>
  11. </dependency>

3.3 由于jwt需要三个属性 密钥 有效期 Token的名称

所以需要配置对应的资源类

  1. @Component
  2. @ConfigurationProperties(prefix = "paitool.jwt")
  3. @Data
  4. public class JwtProperties {
  5. private String SecretKey;
  6. private long Ttl;
  7. private String TokenName;
  8. }

application.yml:

  1. paitool:
  2. jwt:
  3. secret-key: Alphamilk
  4. ttl: 10800000
  5. token-name: Authorization

3.4 创建配置Jwt的工具类 实现快速创建Jwt与解密Jwt方法

  1. public class JwtUtil {
  2. /**
  3. * 生成jwt
  4. * 使用Hs256算法, 私匙使用固定秘钥
  5. *
  6. * @param secretKey jwt秘钥
  7. * @param ttlMillis jwt过期时间(毫秒)
  8. * @param claims 设置的信息
  9. * @return
  10. */
  11. public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
  12. // 指定签名的时候使用的签名算法,也就是header那部分
  13. SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  14. // 生成JWT的时间
  15. long expMillis = System.currentTimeMillis() + ttlMillis;
  16. Date exp = new Date(expMillis);
  17. // 设置jwt的body
  18. JwtBuilder builder = Jwts.builder()
  19. // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
  20. .setClaims(claims)
  21. // 设置签名使用的签名算法和签名使用的秘钥
  22. .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
  23. // 设置过期时间
  24. .setExpiration(exp);
  25. return builder.compact();
  26. }
  27. /**
  28. * Token解密
  29. *
  30. * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
  31. * @param token 加密后的token
  32. * @return
  33. */
  34. public static Claims parseJWT(String secretKey, String token) {
  35. // 得到DefaultJwtParser
  36. Claims claims = Jwts.parser()
  37. // 设置签名的秘钥
  38. .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
  39. // 设置需要解析的jwt
  40. .parseClaimsJws(token).getBody();
  41. return claims;
  42. }
  43. }

3.5  通过ThreadLocal实现后端存储用户信息

  1. public class BaseContext {
  2. public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
  3. public static void setCurrentId(Long id) {
  4. threadLocal.set(id);
  5. }
  6. public static Long getCurrentId() {
  7. return threadLocal.get();
  8. }
  9. public static void removeCurrentId() {
  10. threadLocal.remove();
  11. }
  12. }

3.6 配置jwt的拦截器

注意:这里的HandlerMehtod是org.springframework.web.method包下的

  1. @Component
  2. @Slf4j
  3. public class JwtTokenInterceptor implements HandlerInterceptor {
  4. @Autowired
  5. private JwtProperties jwtProperties;
  6. /**
  7. * 校验jwt
  8. *
  9. * @param request
  10. * @param response
  11. * @param handler
  12. * @return
  13. * @throws Exception
  14. */
  15. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  16. //判断当前拦截到的是Controller的方法还是其他资源
  17. if (!(handler instanceof HandlerMethod)) {
  18. //当前拦截到的不是动态方法,直接放行
  19. return true;
  20. }
  21. //1、从请求头中获取令牌
  22. String token = request.getHeader(jwtProperties.getTokenName());
  23. //2、校验令牌
  24. try {
  25. log.info("jwt校验:{}", token);
  26. Claims claims = JwtUtil.parseJWT(jwtProperties.getSecretKey(), token);
  27. // 获取JWT的过期时间并转换为可读格式
  28. Date expirationDate = claims.getExpiration();
  29. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  30. String formattedExpiration = sdf.format(expirationDate);
  31. log.info("JWT过期时间:{}", formattedExpiration);
  32. Long userId = Long.valueOf(claims.get("userId").toString());
  33. log.info("当前用户id:", userId);
  34. //通过ThreadLocal保存员工id
  35. BaseContext.setCurrentId(userId);
  36. //3、通过,放行
  37. return true;
  38. } catch (Exception ex) {
  39. //4、不通过,响应401状态码
  40. response.setStatus(401);
  41. return false;
  42. }
  43. }
  44. }

3.7 将配置好的拦截器加入到webMvc配置中(由于本次实战通过用户登陆获取token,记得排除用户登陆时候进行校验的过程)

  1. @Configuration
  2. @Slf4j
  3. public class WebMvcConfig extends WebMvcConfigurationSupport {
  4. @Autowired
  5. private JwtTokenInterceptor jwtTokenInterceptor;
  6. @Override
  7. protected void addInterceptors(InterceptorRegistry registry) {
  8. log.info("开始注册自定义拦截器...");
  9. registry.addInterceptor(jwtTokenInterceptor)
  10. .addPathPatterns("/**")
  11. .excludePathPatterns("/user/login")
  12. .excludePathPatterns("/user/GetCaptcha");
  13. }

四、实战

1.创建User表单

  1. create table paitool.user
  2. (
  3. id int auto_increment
  4. primary key,
  5. account varchar(255) not null,
  6. password varchar(255) not null,
  7. phone varchar(20) null,
  8. address varchar(255) null,
  9. isVip tinyint(1) default 0 null,
  10. email varchar(255) null,
  11. registration_date datetime default CURRENT_TIMESTAMP null,
  12. last_login datetime null,
  13. status enum ('active', 'inactive') default 'active' null,
  14. constraint account_UNIQUE
  15. unique (account),
  16. constraint email_UNIQUE
  17. unique (email),
  18. constraint phone_UNIQUE
  19. unique (phone)
  20. );

通过MyBatisPlusX自动生成架构

 2.创建返回结果实体类

  1. //结果类
  2. public class Result<T> {
  3. // 状态码常量
  4. public static final int SUCCESS = 200;
  5. public static final int ERROR = 500;
  6. private int code; // 状态码
  7. private String message; // 消息
  8. private T data; // 数据
  9. // 构造函数,用于创建成功的结果对象
  10. private Result(int code, String message, T data) {
  11. this.code = code;
  12. this.message = message;
  13. this.data = data;
  14. }
  15. // 成功结果的静态方法
  16. public static <T> Result<T> success(T data) {
  17. return new Result<>(SUCCESS, "Success", data);
  18. }
  19. // 错误结果的静态方法
  20. public static <T> Result<T> error(String message) {
  21. return new Result<>(ERROR, message, null);
  22. }
  23. // 错误结果的静态方法,可以传入自定义的状态码
  24. public static <T> Result<T> error(int code, String message) {
  25. return new Result<>(code, message, null);
  26. }
  27. // 获取状态码
  28. public int getCode() {
  29. return code;
  30. }
  31. // 设置状态码
  32. public void setCode(int code) {
  33. this.code = code;
  34. }
  35. // 获取消息
  36. public String getMessage() {
  37. return message;
  38. }
  39. // 设置消息
  40. public void setMessage(String message) {
  41. this.message = message;
  42. }
  43. // 获取数据
  44. public T getData() {
  45. return data;
  46. }
  47. // 设置数据
  48. public void setData(T data) {
  49. this.data = data;
  50. }
  51. // 用于转换为Map类型的方法,方便序列化为JSON
  52. public Map<String, Object> toMap() {
  53. Map<String, Object> map = new HashMap<>();
  54. map.put("code", code);
  55. map.put("message", message);
  56. map.put("data", data);
  57. return map;
  58. }
  59. }

3.创建验证码(防止密码爆破)工具类 与 Md5加密与解密工具类(防止数据库密码信息泄露)

  1. public class CaptchaUtil {
  2. private static final int WIDTH = 200;
  3. private static final int HEIGHT = 75;
  4. private static final int FONT_SIZE = 36;
  5. private static final String DEFAULT_FONT = "Arial";
  6. /**
  7. * 生成验证码图像.
  8. *
  9. * @param captchaText 验证码原始文本
  10. * @return Base64编码的图像字符串
  11. */
  12. public static String generateCaptchaImage(String captchaText) {
  13. if (captchaText == null || captchaText.isEmpty()) {
  14. throw new IllegalArgumentException("Captcha text cannot be null or empty.");
  15. }
  16. // 创建图像和图形上下文
  17. BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
  18. Graphics2D g = (Graphics2D) image.getGraphics();
  19. // 设置背景颜色
  20. g.setColor(Color.WHITE);
  21. g.fillRect(0, 0, WIDTH, HEIGHT);
  22. // 绘制验证码文本
  23. g.setFont(new Font(DEFAULT_FONT, Font.BOLD, FONT_SIZE));
  24. g.setColor(getRandomColor());
  25. g.drawString(captchaText, 45, 50);
  26. // 添加随机线条作为干扰
  27. addNoiseLines(g);
  28. // 关闭图形上下文
  29. g.dispose();
  30. // 将图像转换为Base64编码的字符串
  31. try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
  32. ImageIO.write(image, "png", baos);
  33. return Base64.getEncoder().encodeToString(baos.toByteArray());
  34. } catch (Exception e) {
  35. throw new RuntimeException("Error generating captcha image", e);
  36. }
  37. }
  38. private static void addNoiseLines(Graphics2D g) {
  39. for (int i = 0; i < 5; i++) {
  40. g.setColor(getRandomColor());
  41. g.drawLine(
  42. getRandomNumber(WIDTH),
  43. getRandomNumber(HEIGHT),
  44. getRandomNumber(WIDTH),
  45. getRandomNumber(HEIGHT)
  46. );
  47. }
  48. }
  49. private static Color getRandomColor() {
  50. return new Color((int) (Math.random() * 255),
  51. (int) (Math.random() * 255),
  52. (int) (Math.random() * 255));
  53. }
  54. private static int getRandomNumber(int bound) {
  55. return (int) (Math.random() * bound);
  56. }
  57. }
  1. public final class MD5Util {
  2. /**
  3. * 使用MD5算法对字符串进行加密。
  4. *
  5. * @param input 待加密的字符串
  6. * @return 加密后的MD5散列值字符串
  7. */
  8. public static String encryptToMD5(String input) {
  9. try {
  10. MessageDigest md = MessageDigest.getInstance("MD5");
  11. byte[] hashInBytes = md.digest(input.getBytes());
  12. // 将字节数组转换成十六进制字符串
  13. StringBuilder sb = new StringBuilder();
  14. for (byte b : hashInBytes) {
  15. sb.append(String.format("%02x", b));
  16. }
  17. return sb.toString();
  18. } catch (NoSuchAlgorithmException e) {
  19. throw new RuntimeException("MD5 algorithm not found", e);
  20. }
  21. }
  22. public static void main(String[] args) {
  23. String originalString = "Hello World";
  24. String encryptedString = encryptToMD5(originalString);
  25. System.out.println("Original: " + originalString);
  26. System.out.println("Encrypted: " + encryptedString);
  27. }
  28. }

 4.创建数据传输与视图的实体类

登陆时候,前端传入数据

  1. @Data
  2. public class LoginDTO {
  3. private String account;
  4. private String password;
  5. // 验证码
  6. private String captcha;
  7. }

 验证通过后传给前端的数据

  1. @Data
  2. public class loginVo {
  3. private Integer id;
  4. private String account;
  5. private Integer isvip;
  6. private Object status;
  7. private String token;
  8. }

4.UserController实现登陆功能

  1. @RestController
  2. @Slf4j
  3. @RequestMapping("/user")
  4. public class UserController {
  5. @Autowired
  6. UserService userService;
  7. @Autowired
  8. private JwtProperties jwtProperties;
  9. // 登陆时候获取验证码
  10. @ApiOperation("获取验证码功能")
  11. @GetMapping("/GetCaptcha")
  12. public String GetCaptcha(HttpSession session) {
  13. // 随机生成四位验证码原始数据
  14. String allowedChars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  15. String randomString = generateRandomString(allowedChars, 4);
  16. System.out.println("captchaCode " + randomString);
  17. // 将验证码保存到session中
  18. session.setAttribute("captcha", randomString); // 使用方法参数session
  19. String ImageByBase64 = CaptchaUtil.generateCaptchaImage(randomString);
  20. return ImageByBase64;
  21. }
  22. // 实现登陆功能
  23. @ApiOperation("用户登陆功能")
  24. @PostMapping("/login")
  25. public Result<loginVo> Login(@RequestBody LoginDTO loginDTO, HttpSession session) { // 使用同一个HttpSession参数
  26. String captcha = (String) session.getAttribute("captcha");
  27. log.info("用户调用login方法");
  28. if (loginDTO.getCaptcha() == null || !loginDTO.getCaptcha().equalsIgnoreCase(captcha)) {
  29. session.removeAttribute("captcha");
  30. return Result.error("验证码出错了噢!");
  31. }
  32. // 对密码进行md5加密
  33. String encryptToMD5 = MD5Util.encryptToMD5(loginDTO.getPassword());
  34. LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
  35. lambdaQueryWrapper.eq(User::getAccount, loginDTO.getAccount())
  36. .eq(User::getPassword, encryptToMD5);
  37. User user = userService.getOne(lambdaQueryWrapper);
  38. if (user == null) {
  39. return Result.error("很抱歉,查不到此用户");
  40. }
  41. loginVo loginVo = new loginVo();
  42. BeanUtils.copyProperties(user,loginVo);
  43. Map<String,Object> claims = new HashMap<>();
  44. claims.put("userId",user.getId());
  45. String token = JwtUtil.createJWT(jwtProperties.getSecretKey(), jwtProperties.getTtl(), claims);
  46. loginVo.setToken(token);
  47. return Result.success(loginVo);
  48. }
  49. }

前端账户操作View.vue:

  1. <template>
  2. <div id="Header">
  3. <h3>--PaiTool--</h3>
  4. <div class="header-avatar">
  5. <el-popover placement="bottom" :visible-arrow="false" :visible.sync="showUserInfo">
  6. <div class="userInfo">
  7. <p>用户名:{{ account }}</p>
  8. <p>邮箱:{{ email }}</p>
  9. <p>是否是vip: {{ isVip }}</p>
  10. <p>账号状态:{{ status }}</p>
  11. <!-- 登录按钮 -->
  12. <el-button type="primary" @click="showDialog">登录/注册</el-button>
  13. <!-- 退出按钮 -->
  14. <el-button type="text" @click="confirmQuit">退出</el-button>
  15. <!-- 登录对话框 -->
  16. <el-dialog title="登录与注册" :visible.sync="dialogLoginVisible" width="30%" @close="resetLoginForm" append-to-body
  17. :modal-append-to-body="false">
  18. <el-tabs v-model="activeName" @tab-click="handleClick">
  19. <el-tab-pane label="登陆" name="first">
  20. <el-form :model="loginForm" ref="loginFormRef" label-width="80px">
  21. <el-form-item label="用户名:">
  22. <el-input v-model="loginForm.account"></el-input>
  23. </el-form-item>
  24. <el-form-item label="密码:">
  25. <el-input v-model="loginForm.password" show-password></el-input>
  26. </el-form-item>
  27. <el-form-item label="验证码">
  28. <el-input v-model="loginForm.captcha" style="width: 20%;"></el-input>
  29. <img :src="captchaImageUrl" alt="验证码" @click="refreshCaptcha" id="captchaImage">
  30. </el-form-item>
  31. </el-form>
  32. </el-tab-pane>
  33. <el-tab-pane label="注册" name="second">
  34. <el-form :model="loginForm" ref="registerFormRef" label-width="80px">
  35. <el-form-item label="注册用户:">
  36. <el-input v-model="registerFormRef.account"></el-input>
  37. </el-form-item>
  38. <el-form-item label="注册密码:">
  39. <el-input v-model="registerFormRef.password" show-password></el-input>
  40. </el-form-item>
  41. <el-form-item label="验证码">
  42. <el-input v-model="registerFormRef.captcha" style="width: 20%;"></el-input>
  43. <img :src="captchaImageUrl" alt="验证码" @click="refreshCaptcha" id="captchaImage">
  44. </el-form-item>
  45. </el-form>
  46. </el-tab-pane>
  47. </el-tabs>
  48. <span slot="footer" class="dialog-footer">
  49. <el-button @click="dialogLoginVisible = false">取消</el-button>
  50. <el-button type="primary" @click="submitLogin">登录|注册</el-button>
  51. </span>
  52. </el-dialog>
  53. <!-- 退出确认对话框 -->
  54. <el-dialog title="确认退出" :visible.sync="dialogConfirmVisible" width="30%" @close="dialogConfirmVisible = false"
  55. append-to-body :modal-append-to-body="false">
  56. <span>您确定要退出吗?</span>
  57. <span slot="footer" class="dialog-footer">
  58. <el-button @click="dialogConfirmVisible = false">取消</el-button>
  59. <el-button type="primary" @click="quit">确定退出</el-button>
  60. </span>
  61. </el-dialog>
  62. </div>
  63. <el-avatar slot="reference" :src="circleUrl" :size="40" class="clickable-avatar"></el-avatar>
  64. </el-popover>
  65. </div>
  66. </div>
  67. </template>
  68. <script>
  69. import axios from 'axios';
  70. import Cookies from 'js-cookie';
  71. export default {
  72. data() {
  73. return {
  74. showUserInfo: false, // 控制个人信息弹窗的显示状态
  75. circleUrl: "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
  76. isVip: '否',
  77. account: '未登录',
  78. status: '正常',
  79. email: 'none',
  80. activeName: 'first',
  81. loginOrRegistFlag: true,
  82. dialogLoginVisible: false,
  83. dialogConfirmVisible: false,
  84. loginForm: {
  85. username: '',
  86. password: '',
  87. },
  88. registerFormRef: {
  89. username: '',
  90. password: '',
  91. },
  92. captchaImageUrl: '', // 初始化为一个空字符串
  93. }
  94. },
  95. mounted() {
  96. this.loadUserDataFromCookie();
  97. },
  98. methods: {
  99. loadUserDataFromCookie() {
  100. // 从cookie中读取account
  101. const account = Cookies.get('account');
  102. if (account) {
  103. this.account = account;
  104. }
  105. // 从cookie中读取isVip
  106. const isVip = Cookies.get('isVip');
  107. if (isVip !== undefined) {
  108. // 注意:从cookie读取的数据是字符串类型,需要转换成布尔型
  109. this.isVip = isVip === 'true';
  110. }
  111. // 从cookie中读取status
  112. const status = Cookies.get('status');
  113. if (status) {
  114. this.status = status;
  115. }
  116. // 从cookie中读取email
  117. const email = Cookies.get('email');
  118. if (email) {
  119. this.email = email;
  120. }
  121. },
  122. // 打开登录对话框
  123. open() {
  124. this.dialogLoginVisible = true;
  125. },
  126. resetLoginForm() {
  127. this.$refs.loginFormRef.resetFields();
  128. },
  129. // 提交登录
  130. submitLogin() {
  131. // 判断是注册还是登录
  132. if (this.loginOrRegistFlag == true) {
  133. // 这里添加验证逻辑(如果需要)
  134. console.log('登录表单提交:', this.loginForm);
  135. this.dialogLoginVisible = false;
  136. // 将this.loginForm作为参数上传
  137. axios.post("/api/user/login", this.loginForm)
  138. .then(response => {
  139. console.log(response.data);
  140. if (response.data.code === 500) {
  141. // 重新获取验证码
  142. this.refreshCaptcha();
  143. this.$message.error(response.data.message);
  144. } else if (response.data.code === 200) {
  145. this.$message({
  146. showClose: true,
  147. message: '登陆成功!',
  148. type: 'success'
  149. });
  150. // 设置cookie,可以设置过期时间
  151. Cookies.set('account', response.data.data.account, { expires: 7 });
  152. Cookies.set('isVip', response.data.data.isVip, { expires: 7 });
  153. Cookies.set('status', response.data.data.status, { expires: 7 });
  154. Cookies.set('email', response.data.data.email, { expires: 7 });
  155. Cookies.set('userId', response.data.data.id, { expires: 7 })
  156. localStorage.setItem('token', response.data.data.token);
  157. this.account = response.data.data.account;
  158. this.isVip = response.data.data.isVip;
  159. this.status = response.data.data.status;
  160. this.email = response.data.data.email;
  161. }
  162. })
  163. .catch(error => {
  164. // 处理错误响应
  165. console.error('登录失败:', error);
  166. this.$message.error('登陆错了哦,这是一条错误消息')
  167. });
  168. } else {
  169. axios.post('/api/user/register', this.registerFormRef).then(response => {
  170. if (response.data.code === 200) {
  171. this.$message({
  172. showClose: true,
  173. message: '注册成功!',
  174. type: 'success'
  175. });
  176. this.dialogLoginVisible = false;
  177. } else {
  178. this.$message.error(response.data.message);
  179. }
  180. });
  181. }
  182. },
  183. // 打开退出确认对话框
  184. confirmQuit() {
  185. this.dialogConfirmVisible = true;
  186. },
  187. // 执行退出操作
  188. quit() {
  189. // 这里执行实际的退出逻辑
  190. console.log('执行退出操作');
  191. this.dialogConfirmVisible = false;
  192. // 将Cookie所有字段删除
  193. Cookies.remove('account');
  194. Cookies.remove('isVip');
  195. Cookies.remove('status');
  196. Cookies.remove('email');
  197. Cookies.remove('userId');
  198. this.account = '未登录';
  199. this.isVip = '否';
  200. this.status = '离线';
  201. this.email = 'none';
  202. this.$message({
  203. showClose: true,
  204. message: '退出成功!',
  205. type: 'success'
  206. });
  207. },
  208. // 刷新验证码的示例函数
  209. refreshCaptcha() {
  210. // 实现刷新验证码的逻辑
  211. console.log('刷新验证码');
  212. this.fetchCaptcha();
  213. },
  214. fetchCaptcha() {
  215. axios.get('/api/user/GetCaptcha')
  216. .then(response => {
  217. this.captchaImageUrl = 'data:image/png;base64,' + response.data;
  218. })
  219. .catch(error => {
  220. console.error('获取验证码失败:', error);
  221. });
  222. },
  223. showDialog() {
  224. this.fetchCaptcha(); // 先获取验证码
  225. this.dialogLoginVisible = true; // 然后显示登录对话框
  226. },
  227. handleClick(tab) {
  228. if (tab.name === 'first') {
  229. this.loginOrRegistFlag = true;
  230. } else {
  231. this.loginOrRegistFlag = false;
  232. }
  233. }
  234. }
  235. }
  236. </script>
  237. <style scoped>
  238. h3 {
  239. color: #E9EEF3;
  240. float: left;
  241. width: 1307px;
  242. height: 60px;
  243. margin-left: 15%;
  244. }
  245. .header-avatar {
  246. position: relative;
  247. /* 为绝对定位的子元素提供上下文 */
  248. float: right;
  249. z-index: 1000;
  250. /* 设置一个较高的 z-index 值以确保其位于其他元素之上 */
  251. margin-top: 10px;
  252. }
  253. .clickable-avatar {
  254. /* 添加点击手势效果 */
  255. cursor: pointer;
  256. }
  257. .userInfo {
  258. text-align: left;
  259. padding: 10px;
  260. }
  261. #captchaImage {
  262. cursor: pointer;
  263. width: 136px;
  264. height: 45px;
  265. border: 1px solid black;
  266. float: right;
  267. margin-right: 54%;
  268. }
  269. </style>

 数据库创建用户与(123456)加密后的密码

account: admin

password: e10adc3949ba59abbe56e057f20f883e

进入前端并进行登陆

 查看返回结果的token,前端的login函数已经自动存入了token中了

使用其它功能,查看是否有效

这里看到,后端正常识别到并解析出来了。


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/989910
推荐阅读
相关标签
  

闽ICP备14008679号