当前位置:   article > 正文

SpringBoot整合jwt+redis+随机验证码+Vue的登录功能_jwt 验证码

jwt 验证码

一、运行效果展示

!注意:前端的Vue项目中要引入element-ui和axios

# npm安装element-ui、axios

npm insatll element-ui -S

npm install axios -S

# 在main中引入

// 引入ElementUI
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

// 使用axios
import axios from 'axios'
axios.defaults.baseURL = 'http://127.0.0.1:'
Vue.prototype.$axios = axios

二、环境依赖准备

1、引入pom依赖

  1. <!-- jwt -->
  2. <dependency>
  3. <groupId>com.auth0</groupId>
  4. <artifactId>java-jwt</artifactId>
  5. <version>3.18.2</version>
  6. </dependency>
  7. <!-- redis -->
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-data-redis</artifactId>
  11. </dependency>
  12. <!-- lombok -->
  13. <dependency>
  14. <groupId>org.projectlombok</groupId>
  15. <artifactId>lombok</artifactId>
  16. <optional>true</optional>
  17. </dependency>

2、配置yaml

  1. server:
  2. port: 8080
  3. spring:
  4. application:
  5. name: login-service # 服务名称
  6. redis:
  7. host: 127.0.0.1
  8. port: 6379
  9. password: 123456
  10. database: 0 #操作的是0号数据库
  11. jedis:
  12. #Redis连接池配置
  13. pool:
  14. max-active: 8 #最大连接数
  15. max-wait: 1ms #连接池最大阻塞等待时间
  16. max-idle: 4 #连接池中的最大空闲连接
  17. min-idle: 0 #连接池中的最小空闲连接

三、jwt生成与验证token

1、编写token工具类TokenUtils

  1. import com.auth0.jwt.JWT;
  2. import com.auth0.jwt.JWTCreator;
  3. import com.auth0.jwt.algorithms.Algorithm;
  4. import com.auth0.jwt.exceptions.TokenExpiredException;
  5. import lombok.Data;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.data.redis.core.StringRedisTemplate;
  9. import org.springframework.data.redis.core.ValueOperations;
  10. import org.springframework.stereotype.Component;
  11. import java.util.Calendar;
  12. import java.util.concurrent.TimeUnit;
  13. @Component
  14. @Data
  15. @Slf4j
  16. public class TokenUtils {
  17. @Autowired
  18. private StringRedisTemplate stringRedisTemplate;
  19. // 创建Token
  20. public String createToken(String userName, String hostIp) {
  21. //时间工具类
  22. Calendar instance = Calendar.getInstance();
  23. //设置过期时间 单位:SECOND秒 3个小时失效
  24. instance.add(Calendar.SECOND, 3 * 60 * 60);
  25. //签名(自定义)
  26. Algorithm algorithm = Algorithm.HMAC256("buliangshuai");
  27. // 创建token
  28. JWTCreator.Builder builder = JWT.create()
  29. //添加键值对数据
  30. .withClaim("userName", userName)
  31. //添加过期时间
  32. .withExpiresAt(instance.getTime());
  33. // 选择签名算法HMAC256,添加密钥字符串签名
  34. String token = builder.sign(algorithm);
  35. //输出token
  36. System.out.println("用户" + userName + "的token是:" + token);
  37. // 将token存入redis
  38. ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
  39. // 存入主机IP和token,指定过期时间
  40. forValue.set(hostIp+"token", token, 3 * 60 * 60, TimeUnit.SECONDS);
  41. return token;
  42. }
  43. // 验证Token
  44. public boolean verifyToken(String token, String hostIp) {
  45. try {
  46. // 根据主机地址和redis中存储的值比对判断token是否正确
  47. String redisToken = stringRedisTemplate.boundValueOps(hostIp + "token").get();
  48. if(!token.equals(redisToken)){
  49. return false;
  50. }
  51. } catch (TokenExpiredException e) {
  52. //令牌过期抛出异常
  53. System.out.println("令牌过期");
  54. return false;
  55. } catch (Exception e) {
  56. //token非法验证失败抛出异常
  57. System.out.println("检验失败");
  58. return false;
  59. }
  60. return true;
  61. }
  62. // 解密token
  63. public String decodeToken(String token){
  64. String userName = JWT.require(Algorithm.HMAC256("buliangshuai")).build().verify(token).getClaim("userName").asString();
  65. return userName;
  66. }
  67. }

2、编写token接口控制类

  1. import com.blywl.common.utils.TokenUtils;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.web.bind.annotation.*;
  5. import javax.servlet.http.HttpServletRequest;
  6. @RestController
  7. @RequestMapping("/test")
  8. @Slf4j
  9. @CrossOrigin // 跨域
  10. public class TestController {
  11. @Autowired
  12. private TokenUtils tokenUtils;
  13. // 创建token
  14. @PostMapping("/token")
  15. public String createToken(@RequestParam("userName") String userName, HttpServletRequest request){
  16. return tokenUtils.createToken(userName, request.getRemoteAddr());
  17. }
  18. // 验证token
  19. @PostMapping("/verifyToken")
  20. public boolean verifyToken(@RequestParam("token") String token, HttpServletRequest request){
  21. return tokenUtils.verifyToken(token, request.getRemoteAddr());
  22. }
  23. }

 3、前端api调用

  1. import axios from 'axios'
  2. let hostIp= "http://127.0.0.1:"
  3. export default {
  4. // 生成用户Token
  5. createToken(data) {
  6. return axios({
  7. url: hostIp + '8080/test/token',
  8. params: data,
  9. method: 'post'
  10. })
  11. }
  12. }

四、随机验证码

1、验证码工具类codeUtils

  1. import javax.imageio.ImageIO;
  2. import java.awt.*;
  3. import java.awt.image.BufferedImage;
  4. import java.io.IOException;
  5. import java.io.OutputStream;
  6. import java.util.Random;
  7. /**
  8. * code验证码生成工具类
  9. */
  10. public class CodeUtils {
  11. /**
  12. * 生成验证码图片的宽度
  13. */
  14. private int width = 100;
  15. /**
  16. * 生成验证码图片的高度
  17. */
  18. private int height = 30;
  19. /**
  20. * 字符样式
  21. */
  22. private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
  23. /**
  24. * 定义验证码图片的背景颜色为白色
  25. */
  26. private Color bgColor = new Color(255, 255, 255);
  27. /**
  28. * 生成随机
  29. */
  30. private Random random = new Random();
  31. /**
  32. * 定义code字符
  33. */
  34. private String codes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  35. /**
  36. * 记录随机字符串
  37. */
  38. private String text;
  39. /**
  40. * 获取一个随意颜色
  41. * @return
  42. */
  43. private Color randomColor() {
  44. int red = random.nextInt(150);
  45. int green = random.nextInt(150);
  46. int blue = random.nextInt(150);
  47. return new Color(red, green, blue);
  48. }
  49. /**
  50. * 获取一个随机字体
  51. *
  52. * @return
  53. */
  54. private Font randomFont() {
  55. String name = fontNames[random.nextInt(fontNames.length)];
  56. int style = random.nextInt(4);
  57. int size = random.nextInt(5) + 24;
  58. return new Font(name, style, size);
  59. }
  60. /**
  61. * 获取一个随机字符
  62. *
  63. * @return
  64. */
  65. private char randomChar() {
  66. return codes.charAt(random.nextInt(codes.length()));
  67. }
  68. /**
  69. * 创建一个空白的BufferedImage对象
  70. *
  71. * @return
  72. */
  73. private BufferedImage createImage() {
  74. BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  75. Graphics2D g2 = (Graphics2D) image.getGraphics();
  76. //设置验证码图片的背景颜色
  77. g2.setColor(bgColor);
  78. g2.fillRect(0, 0, width, height);
  79. return image;
  80. }
  81. public BufferedImage getImage() {
  82. BufferedImage image = createImage();
  83. Graphics2D g2 = (Graphics2D) image.getGraphics();
  84. StringBuffer sb = new StringBuffer();
  85. for (int i = 0; i < 4; i++) {
  86. String s = randomChar() + "";
  87. sb.append(s);
  88. g2.setColor(randomColor());
  89. g2.setFont(randomFont());
  90. float x = i * width * 1.0f / 4;
  91. g2.drawString(s, x, height - 8);
  92. }
  93. this.text = sb.toString();
  94. drawLine(image);
  95. return image;
  96. }
  97. /**
  98. * 绘制干扰线
  99. *
  100. * @param image
  101. */
  102. private void drawLine(BufferedImage image) {
  103. Graphics2D g2 = (Graphics2D) image.getGraphics();
  104. int num = 5;
  105. for (int i = 0; i < num; i++) {
  106. int x1 = random.nextInt(width);
  107. int y1 = random.nextInt(height);
  108. int x2 = random.nextInt(width);
  109. int y2 = random.nextInt(height);
  110. g2.setColor(randomColor());
  111. g2.setStroke(new BasicStroke(1.5f));
  112. g2.drawLine(x1, y1, x2, y2);
  113. }
  114. }
  115. public String getText() {
  116. return text;
  117. }
  118. public static void output(BufferedImage image, OutputStream out) throws IOException {
  119. ImageIO.write(image, "JPEG", out);
  120. }
  121. }

2、编写验证码接口控制类

  1. import com.blywl.common.utils.CodeUtils;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.data.redis.core.StringRedisTemplate;
  4. import org.springframework.data.redis.core.ValueOperations;
  5. import org.springframework.web.bind.annotation.*;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.awt.image.BufferedImage;
  9. import java.io.IOException;
  10. import java.util.concurrent.TimeUnit;
  11. @RestController
  12. @RequestMapping("/login")
  13. @CrossOrigin // 跨域
  14. public class LoginController {
  15. @Autowired
  16. private StringRedisTemplate stringRedisTemplate;
  17. /**
  18. * 生成验证码图片
  19. */
  20. @GetMapping("/code")
  21. public void code(HttpServletRequest request, HttpServletResponse res) throws IOException {
  22. CodeUtils code = new CodeUtils();
  23. // 生成验证码图片
  24. BufferedImage image = code.getImage();
  25. // 将验证码text存入redis中
  26. String text = code.getText();
  27. ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
  28. String hostIp = request.getRemoteAddr() + "code";
  29. // 主机,code,3分钟过期
  30. forValue.set(hostIp, text, 3 * 60, TimeUnit.SECONDS);
  31. // 响应验证码图片
  32. CodeUtils.output(image, res.getOutputStream());
  33. }
  34. /**
  35. * 登录
  36. */
  37. @PostMapping("/login")
  38. public String login(@RequestParam("code") String code, HttpServletRequest request) {
  39. // 根据主机地址和redis中存储的值比对判断验证码是否正确
  40. String hostIp = request.getRemoteAddr() + "code";
  41. String redisCode = stringRedisTemplate.boundValueOps(hostIp).get();
  42. System.out.println("redisValue:" + redisCode);
  43. if (code.equalsIgnoreCase(redisCode)) {
  44. return "登录成功!";
  45. }
  46. return "登录失败~";
  47. }
  48. }

3、前端api调用

  1. // 登录
  2. async login(data) {
  3. return axios({
  4. url: hostIp + '8080/login/login',
  5. params: data,
  6. method: 'post'
  7. })
  8. },

 五、完整前端代码

Login.vue

  1. <template>
  2. <div class="login">
  3. <!-- 卡片 -->
  4. <el-card class="box-card">
  5. <h1 style="margin: 0 0 14px 100px">登录页面</h1>
  6. <!-- 登录 or 注册 -->
  7. <el-radio-group v-model="labelPosition" class="radioGroup" size="small">
  8. <el-radio-button label="login">登录</el-radio-button>
  9. <el-radio-button label="signIn">注册</el-radio-button>
  10. </el-radio-group>
  11. <!-- user输入表单 -->
  12. <el-form label-position="right" label-width="80px" :model="user">
  13. <el-form-item
  14. label="用户名"
  15. prop="name"
  16. :rules="[ { required: true, message: '请输入用户名', trigger: 'blur' } ]">
  17. <el-input v-model="user.name"></el-input>
  18. </el-form-item>
  19. <el-form-item
  20. label="密码"
  21. prop="password"
  22. :rules="[ { required: true, message: '请输入密码', trigger: 'blur' } ]">
  23. <el-input type="password" v-model="user.password" show-password></el-input>
  24. </el-form-item>
  25. <el-form-item
  26. v-if="labelPosition==='signIn'"
  27. label="确认密码"
  28. prop="checkPassword"
  29. :rules="[ { required: true, message: '请输入再次输入密码', trigger: 'blur' } ]">
  30. <el-input type="password" v-model="user.checkPassword" show-password></el-input>
  31. </el-form-item>
  32. <el-form-item
  33. label="验证码"
  34. prop="code"
  35. :rules="[ { required: true, message: '请输入验证码', trigger: 'blur' } ]">
  36. <el-input v-model="user.code" style="width: 120px"></el-input>
  37. <el-image class="codeImg" :src="imgUrl" style="cursor: pointer" @click="resetImg"></el-image>
  38. </el-form-item>
  39. <!--按钮-->
  40. <el-form-item class="button">
  41. <el-button class="button1" v-if="labelPosition==='login'" type="warning" @click="login"
  42. :disabled="user.name===''||user.password===''||user.code===''" >登录
  43. </el-button>
  44. <el-button class="button1" v-if="labelPosition==='signIn'" type="warning" @click="signIn"
  45. :disabled="user.name===''||user.password===''||user.checkPassword===''||user.code===''">注册
  46. </el-button>
  47. <el-button class="button1" @click="resetForm">重置</el-button>
  48. </el-form-item>
  49. </el-form>
  50. </el-card>
  51. </div>
  52. </template>
  53. <script>
  54. import logApi from "../assets/api/loginApi"
  55. export default {
  56. name: "Login",
  57. data() {
  58. return {
  59. labelPosition: 'login', // 开始先定位到登录
  60. // 用户数据
  61. user: {
  62. name: '',
  63. password: '',
  64. checkPassword: '',
  65. code: '' // 验证码
  66. },
  67. imgUrl: '',
  68. }
  69. },
  70. // 创建周期函数
  71. created() {
  72. // 获取验证码图片
  73. this.imgUrl = this.$axios.defaults.baseURL + "8080/login/code"
  74. },
  75. methods: {
  76. // 登录
  77. login() {
  78. // 生成token并存储在浏览器内存中(可以使用localStorage.getItem('token'))查看token)
  79. logApi.createToken({"userName": this.user.name}).then( r =>
  80. localStorage.setItem("token", r.data)
  81. )
  82. // 验证码校验
  83. logApi.login({"code": this.user.code,}).then( r =>
  84. this.$message.info(r.data)
  85. )
  86. },
  87. // 注册
  88. signIn() {
  89. if (this.user.checkPassword !== this.user.password) {
  90. this.$message.error("两次输入的密码不一致!")
  91. }
  92. },
  93. // 点击刷新验证码图片
  94. resetImg(){
  95. this.imgUrl = this.$axios.defaults.baseURL + "8080/login/code?time="+new Date();
  96. },
  97. // 重置表单
  98. resetForm() {
  99. this.user.name = ""
  100. this.user.password = ""
  101. this.user.checkPassword = ""
  102. }
  103. }
  104. }
  105. </script>
  106. <style>
  107. .login{
  108. width: 100%;
  109. height: 100%;
  110. /*position: fixed;*/
  111. /*background-image: url(~@/assets/images/login.png);*/
  112. /*background-repeat: no-repeat;*/
  113. /*background-size: cover;*/
  114. }
  115. .box-card {
  116. width: 370px;
  117. margin: 5% auto auto auto;
  118. border: 1px solid #1f808c;
  119. }
  120. .radioGroup{
  121. width: 100%;
  122. margin: 0 0 10px 120px;
  123. }
  124. .codeImg{
  125. width: 120px;
  126. height: 35px;
  127. position: relative;
  128. top: 13px;
  129. left: 10px;
  130. border: 1px solid #b7b7b7;
  131. }
  132. .button{
  133. width: 100%;
  134. margin: 0 0 0 -25px;
  135. }
  136. .button1{
  137. width: 120px;
  138. }
  139. </style>

loginApi.js

  1. import axios from 'axios'
  2. let hostIp= "http://127.0.0.1:"
  3. export default {
  4. // 登录
  5. async login(data) {
  6. return axios({
  7. url: hostIp + '8080/login/login',
  8. params: data,
  9. method: 'post'
  10. })
  11. },
  12. // 生成用户Token
  13. createToken(data) {
  14. return axios({
  15. url: hostIp + '8080/test/token',
  16. params: data,
  17. method: 'post'
  18. })
  19. }
  20. }

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

闽ICP备14008679号