SpringBoot+Vue 登录(含验证码)、注册功能

ゞ 浴缸里的玫瑰 2023-10-15 08:05 135阅读 0赞

一、初始化项目

参考下面文章,初始vue项目并且引入ElementUI组件

网页制作神器—ElementUI(小白入门超详细)

并且参考基于Axios完成前后端分离项目数据交互 完成 request 请求工具的封装。

最终Vue.js 框架的应用程序的入口文件 main.js 的代码如下:

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. import router from './router'
  4. import ElementUI from 'element-ui';
  5. import 'element-ui/lib/theme-chalk/index.css';
  6. import '@/assets/css/global.css'
  7. import '@/assets/css/iconfont/iconfont.css'
  8. import '@/assets/css/theme/index.css'
  9. import request from "@/utils/request";
  10. // 首先,导入了 Vue、App 组件、router、ElementUI 组件库以及相关CSS 文件。
  11. // 其中,Vue 是 Vue.js 框架的核心库,App 是根组件,router 是路由配置文件,ElementUI 是基于 Vue.js 的 UI 组件库,包括一些常用的界面元素和样式。
  12. //设置了 Vue.config.productionTip 的值为 false,这是对 Vue 配置项的修改,表示阻止在生产环境下产生提示信息。
  13. Vue.config.productionTip = false
  14. // 通过 Vue.use(ElementUI, { size: 'small' }) 使用 ElementUI 插件,并设置全局的默认组件尺寸为 small。
  15. Vue.use(ElementUI, { size: 'small' });
  16. // 通过Vue.prototype.request=request将自定义的request对象挂载到Vue实例的原型上,以便在组件中可以通过this.request 访问到该对象。
  17. // 这个request对象可能是用于发送HTTP请求的工具类或者封装了接口请求的方法。
  18. Vue.prototype.$request=request
  19. // 创建了一个新的 Vue 实例,配置了 router 和 App 组件,并通过 $mount 方法将实例挂载到 id 为 app 的 HTML 元素上,实现应用程序的渲染和启动。
  20. new Vue({
  21. router,
  22. render: h => h(App) // 行代码导入了名为 App 的根组件,并在 render 函数中将其渲染到页面上。h 是 createElement 的别名,用于创建虚拟 DOM 元素
  23. }).$mount('#app')

render: h => h(App) 是 Vue.js 中的渲染函数 (render function) 的书写方式。它使用箭头函数的语法规则,表示一个匿名函数,接收一个参数 h,并返回 h(App)。在 Vue.js 中,组件的模板可以通过 template 属性来定义,也可以使用渲染函数进行动态渲染。渲染函数可以通过编程方式创建虚拟 DOM,并最终渲染成实际的 DOM 元素。在这里,h 实际上是 createElement 的缩写,它是一个用于创建虚拟 DOM 元素的函数。h(App) 表示调用 createElement 函数来创建一个根据 App 组件生成的虚拟 DOM 对象。

所以,render: h => h(App) 的意思是将 App 组件渲染为虚拟 DOM 元素,并最终展示在页面上。这种写法在单文件组件中常见,用于使用渲染函数进行动态渲染的情况。

当然还要引入一个验证码插件:ValidCode.vue

  1. <template>
  2. <div class="ValidCode disabled-select" style="width: 100%; height: 100%" @click="refreshCode">
  3. <span v-for="(item, index) in codeList" :key="index" :style="getStyle(item)">{
  4. {item.code}}</span>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. name: 'validCode',
  10. data () {
  11. return {
  12. length: 4,
  13. codeList: []
  14. }
  15. },
  16. mounted () {
  17. this.createdCode()
  18. },
  19. methods: {
  20. refreshCode () {
  21. this.createdCode()
  22. },
  23. createdCode () {
  24. let len = this.length,
  25. codeList = [],
  26. chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz0123456789',
  27. charsLen = chars.length
  28. // 生成
  29. for (let i = 0; i < len; i++) {
  30. let rgb = [Math.round(Math.random() * 220), Math.round(Math.random() * 240), Math.round(Math.random() * 200)]
  31. codeList.push({
  32. code: chars.charAt(Math.floor(Math.random() * charsLen)),
  33. color: `rgb(${rgb})`,
  34. padding: `${[Math.floor(Math.random() * 10)]}px`,
  35. transform: `rotate(${Math.floor(Math.random() * 90) - Math.floor(Math.random() * 90)}deg)`
  36. })
  37. }
  38. // 指向
  39. this.codeList = codeList
  40. // 将当前数据派发出去
  41. this.$emit('update:value', codeList.map(item => item.code).join(''))
  42. },
  43. getStyle (data) {
  44. return `color: ${data.color}; font-size: ${data.fontSize}; padding: ${data.padding}; transform: ${data.transform}`
  45. }
  46. }
  47. }
  48. </script>
  49. <style>
  50. .ValidCode{
  51. display: flex;
  52. justify-content: center;
  53. align-items: center;
  54. cursor: pointer;
  55. }
  56. .ValidCode span {
  57. display: inline-block;
  58. font-size: 18px;
  59. }
  60. </style>

在登录、注册界面处进行引用: import ValidCode from “@/conponents/ValidCode”;即可

二、登录界面:

  1. <template>
  2. <div style="height: 100vh; display: flex; align-items: center; justify-content: center; background-color: #0f9876">
  3. <div style="display: flex; background-color: white; width: 50%; border-radius: 5px; overflow: hidden">
  4. <div style="flex: 1">
  5. <img src="@/assets/login.png" alt="" style="width: 100%">
  6. </div>
  7. <div style="flex: 1; display: flex; align-items: center; justify-content: center">
  8. <el-form :model="user" style="width: 80%" :rules="rules" ref="loginRef">
  9. <div style="font-size: 20px; font-weight: bold; text-align: center; margin-bottom: 20px">欢迎登录后台管理系统</div>
  10. <!--在 Element UI 的表单组件中,prop 是用于指定表单项对应的数据对象中的属性名。它的作用是将该表单项与数据对象进行绑定,实现数据的双向绑定以及表单验证。
  11. 具体来说,当你在 <el-form-item> 中使用 prop="username" 时,它实际上告诉 Element UI 表单组件,该表单项对应的数据对象中的属性名是 username。
  12. 此外,prop 还有一个重要的作用是进行表单验证。你可以定义一些验证规则(通过 :rules 属性传递一个数组),然后在表单提交或表单项失去焦点时,Element UI 会自动根据这些验证规则验证表单项的值,并显示相应的错误信息。
  13. 综上所述,prop 用于指定表单项对应的数据对象的属性名,实现数据的双向绑定,同时也用于进行表单验证。-->
  14. <el-form-item prop="username">
  15. <!--<el-form-item prop="username"> 定义了一个表单项,并将它与 user 数据对象中名为 username 的属性进行关联。这样,当用户输入文本框中的内容时,v-model="user.username" 将实现将输入的值同步到 user.username 属性中。-->
  16. <el-input prefix-icon="el-icon-user" size="medium" placeholder="请输入账号" v-model="user.username"></el-input>
  17. </el-form-item>
  18. <el-form-item prop="password">
  19. <el-input prefix-icon="el-icon-lock" size="medium" show-password placeholder="请输入密码" v-model="user.password"></el-input>
  20. </el-form-item>
  21. <el-form-item prop="code">
  22. <div style="display: flex">
  23. <el-input placeholder="请输入验证码" prefix-icon="el-icon-circle-check" size="medium" style="flex: 1" v-model="user.code"></el-input>
  24. <div style="flex: 1; height: 36px">
  25. <!--是一个自定义组件ValidCode的使用方式,并且添加了一个事件监听器。这里的@update:value是监听了ValidCode组件中的value属性更新事件,并将更新后的值传递给名为getCode的方法。
  26. 当ValidCode组件中的value属性更新时,会触发getCode方法。getCode方法可在父组件中定义,用于处理接收到的验证码信息或进行相关操作。-->
  27. <valid-code @update:value="getCode" />
  28. </div>
  29. </div>
  30. </el-form-item>
  31. <el-form-item>
  32. <el-button type="primary" style="width: 100%" @click="login">登 录</el-button>
  33. </el-form-item>
  34. <div style="display: flex">
  35. <div style="flex: 1">还没有账号?请 <span style="color: #0f9876; cursor: pointer" @click="$router.push('/register')">注册</span></div>
  36. <div style="flex: 1; text-align: right"><span style="color: #0f9876; cursor: pointer" @click="handleForgetPass">忘记密码</span></div>
  37. </div>
  38. </el-form>
  39. </div>
  40. </div>
  41. <el-dialog title="忘记密码" :visible.sync="forgetPassDialogVis" width="30%">
  42. <el-form :model="forgetUserForm" label-width="80px" style="padding-right: 20px">
  43. <el-form-item label="用户名">
  44. <el-input v-model="forgetUserForm.username" autocomplete="off" placeholder="请输入用户名"></el-input>
  45. </el-form-item>
  46. <el-form-item label="手机号">
  47. <el-input v-model="forgetUserForm.phone" autocomplete="off" placeholder="请输入手机号"></el-input>
  48. </el-form-item>
  49. </el-form>
  50. <div slot="footer" class="dialog-footer">
  51. <el-button @click="forgetPassDialogVis = false">取 消</el-button>
  52. <el-button type="primary" @click="resetPassword">确 定</el-button>
  53. </div>
  54. </el-dialog>
  55. </div>
  56. </template>
  57. <script>
  58. import ValidCode from "@/conponents/ValidCode";
  59. // 在components属性中,将ValidCode作为一个组件注册进来。这样就可以在模板中使用<valid-code>标签。
  60. export default {
  61. name: "Login",
  62. components: {
  63. ValidCode
  64. },
  65. data() {
  66. // 验证码校验
  67. const validateCode = (rule, value, callback) => {
  68. if (value === '') {
  69. callback(new Error('请输入验证码'))
  70. } else if (value.toLowerCase() !== this.code) {
  71. callback(new Error('验证码错误'))
  72. } else {
  73. callback()
  74. }
  75. }
  76. return {
  77. forgetUserForm: {}, // 忘记密码的表单数据
  78. forgetPassDialogVis: false,
  79. code: '', // 验证码组件传递过来的code
  80. user: {
  81. code: '', // 表单里用户输入的code 验证码
  82. username: '',
  83. password: ''
  84. },
  85. // trigger属性指定了触发校验的事件类型,这里是在字段失去焦点时触发校验(blur事件)。
  86. rules: {
  87. username: [
  88. { required: true, message: '请输入账号', trigger: 'blur' },
  89. ],
  90. password: [
  91. { required: true, message: '请输入密码', trigger: 'blur' },
  92. ],
  93. code: [
  94. { validator: validateCode, trigger: 'blur' }
  95. ],
  96. }
  97. }
  98. },
  99. created() {
  100. },
  101. methods: {
  102. handleForgetPass() { // 初始化表单的数据
  103. this.forgetUserForm = {}
  104. this.forgetPassDialogVis = true
  105. },
  106. resetPassword() {
  107. this.$request.put('/password', this.forgetUserForm).then(res => {
  108. if (res.code === '200') {
  109. this.$message.success('重置成功')
  110. this.forgetPassDialogVis = false
  111. } else {
  112. this.$message.error(res.msg)
  113. }
  114. })
  115. },
  116. getCode(code) {
  117. this.code = code.toLowerCase()
  118. },
  119. login() {
  120. this.$refs['loginRef'].validate((valid) => {
  121. if (valid) {
  122. // 验证通过
  123. this.$request.post('/login', this.user).then(res => {
  124. if (res.code === '200') {
  125. this.$router.push('/')
  126. this.$message.success('登录成功')
  127. localStorage.setItem("honey-user", JSON.stringify(res.data)) // 存储用户数据
  128. } else {
  129. this.$message.error(res.msg)
  130. }
  131. })
  132. }
  133. })
  134. }
  135. }
  136. }
  137. </script>
  138. <style scoped>
  139. </style>

这是一个使用Vue.js编写的登录页面组件,主要包括一个表单和验证码功能。用户可以输入账号、密码和验证码进行登录操作。

在模板部分,使用了Flex布局将登录框居中显示,并设置了背景颜色。登录框内部分为左右两个部分,左侧为图片展示,右侧为登录表单。登录表单使用了Element UI组件库的el-formel-input组件,分别用于输入账号、密码和验证码。验证码部分还引入了自定义组件ValidCode来生成验证码。登录按钮点击后会触发login方法进行登录操作。下方还有一个注册和忘记密码的链接。

在脚本部分,定义了validateCode方法来验证用户输入的验证码是否正确,并在data中定义了相关数据和验证规则。getCode方法用于接收验证码组件传递过来的验证码值。login方法调用后台接口进行登录操作,登录成功后跳转到首页,并将用户数据存储在本地。

$router.push('/register') 是Vue.js中的路由跳转方法。在这段代码中,点击注册链接时会调用$router.push('/register')方法,将当前页面的路由跳转到/register,即注册页面。

$router 是Vue Router提供的路由实例,它包含了一些用于导航的方法,如push用于向history栈中添加新的记录,并跳转到指定的地址。

所以,$router.push('/register') 的作用是跳转到注册页面。当用户点击“还没有账号?请注册”时,就会触发这个方法并跳转到注册页面。

三、注册界面

  1. <template>
  2. <div style="height: 100vh; display: flex; align-items: center; justify-content: center; background-color: #669fef">
  3. <div style="display: flex; background-color: white; width: 50%; border-radius: 5px; overflow: hidden">
  4. <div style="flex: 1">
  5. <img src="@/assets/register.png" alt="" style="width: 100%">
  6. </div>
  7. <div style="flex: 1; display: flex; align-items: center; justify-content: center">
  8. <el-form :model="user" style="width: 80%" :rules="rules" ref="registerRef">
  9. <div style="font-size: 20px; font-weight: bold; text-align: center; margin-bottom: 20px">欢迎注册后台管理系统</div>
  10. <el-form-item prop="username">
  11. <el-input prefix-icon="el-icon-user" size="medium" placeholder="请输入账号" v-model="user.username"></el-input>
  12. </el-form-item>
  13. <el-form-item prop="password">
  14. <el-input prefix-icon="el-icon-lock" size="medium" show-password placeholder="请输入密码" v-model="user.password"></el-input>
  15. </el-form-item>
  16. <el-form-item prop="confirmPass">
  17. <el-input prefix-icon="el-icon-lock" size="medium" show-password placeholder="请确认密码" v-model="user.confirmPass"></el-input>
  18. </el-form-item>
  19. <el-form-item prop="role">
  20. <el-radio-group v-model="user.role">
  21. <el-radio label="用户"></el-radio>
  22. <el-radio label="商家"></el-radio>
  23. </el-radio-group>
  24. </el-form-item>
  25. <el-form-item>
  26. <el-button type="info" style="width: 100%" @click="register">注 册</el-button>
  27. </el-form-item>
  28. <div style="display: flex">
  29. <div style="flex: 1">已经有账号了?请 <span style="color: #6e77f2; cursor: pointer" @click="$router.push('/login')">登录</span></div>
  30. </div>
  31. </el-form>
  32. </div>
  33. </div>
  34. </div>
  35. </template>
  36. <script>
  37. // export default 的作用是将整个组件对象作为模块的默认导出,方便其他文件引入和使用。
  38. export default {
  39. name: "Register",
  40. data() {
  41. // 验证码校验
  42. const validatePassword = (rule, confirmPass, callback) => {
  43. if (confirmPass === '') {
  44. callback(new Error('请确认密码'))
  45. } else if (confirmPass !== this.user.password) {
  46. callback(new Error('两次输入的密码不一致'))
  47. } else {
  48. callback()
  49. }
  50. }
  51. return {
  52. user: {
  53. username: '',
  54. password: '',
  55. confirmPass: ''
  56. },
  57. rules: {
  58. username: [
  59. { required: true, message: '请输入账号', trigger: 'blur' },
  60. ],
  61. password: [
  62. { required: true, message: '请输入密码', trigger: 'blur' },
  63. ],
  64. confirmPass: [
  65. { validator: validatePassword, trigger: 'blur' }
  66. ],
  67. role: [
  68. { required: true, message: '请选择角色', trigger: 'blur' },
  69. ],
  70. }
  71. }
  72. },
  73. created() {
  74. },
  75. methods: {
  76. register() {
  77. this.$refs['registerRef'].validate((valid) => {
  78. if (valid) {
  79. // 验证通过
  80. this.$request.post('/register', this.user).then(res => {
  81. if (res.code === '200') {
  82. this.$router.push('/login')
  83. this.$message.success('注册成功')
  84. } else {
  85. this.$message.error(res.msg)
  86. }
  87. })
  88. }
  89. })
  90. }
  91. }
  92. }
  93. </script>
  94. <style scoped>
  95. </style>

四、后端接口

  1. @RestController
  2. public class WebController {
  3. @Resource
  4. UserService userService;
  5. @PostMapping("/login")
  6. public Result login(@RequestBody User user) {
  7. if (StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword())) {
  8. return Result.error("数据输入不合法");
  9. }
  10. user = userService.login(user);
  11. return Result.success(user);
  12. }
  13. @AuthAccess
  14. @PostMapping("/register")
  15. public Result register(@RequestBody User user) {
  16. if (StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword()) || StrUtil.isBlank(user.getRole())) {
  17. return Result.error("数据输入不合法");
  18. }
  19. if (user.getUsername().length() > 10 || user.getPassword().length() > 20) {
  20. return Result.error("数据输入不合法");
  21. }
  22. user = userService.register(user);
  23. return Result.success(user);
  24. }
  25. }

当然,此处还要引入一个 国产的工具类依赖:(或者使用别的工具包也行)

  1. <dependency>
  2. <groupId>cn.hutool</groupId>
  3. <artifactId>hutool-all</artifactId>
  4. <version>5.8.18</version>
  5. </dependency>
  6. @Service
  7. public class UserService {
  8. @Autowired
  9. UserMapper userMapper;
  10. // 验证用户账户是否合法
  11. public User login(User user) {
  12. // 根据用户名查询数据库的用户信息
  13. User dbUser = userMapper.selectByUsername(user.getUsername());
  14. if (dbUser == null) {
  15. // 抛出一个自定义的异常
  16. throw new ServiceException("用户名或密码错误");
  17. }
  18. if (!user.getPassword().equals(dbUser.getPassword())) {
  19. throw new ServiceException("用户名或密码错误");
  20. }
  21. // 生成token
  22. String token = TokenUtils.createToken(dbUser.getId().toString(), dbUser.getPassword());
  23. dbUser.setToken(token);
  24. return dbUser;
  25. }
  26. public User register(User user) {
  27. User dbUser = userMapper.selectByUsername(user.getUsername());
  28. if (dbUser != null) {
  29. // 抛出一个自定义的异常
  30. throw new ServiceException("用户名已存在");
  31. }
  32. user.setName(user.getUsername());
  33. userMapper.insert(user);
  34. return user;
  35. }
  36. }

全局异常和自定义异常:

  1. package com.example.springboot.exception;
  2. import com.example.springboot.common.Result;
  3. import org.springframework.web.bind.annotation.ControllerAdvice;
  4. import org.springframework.web.bind.annotation.ExceptionHandler;
  5. import org.springframework.web.bind.annotation.ResponseBody;
  6. @ControllerAdvice
  7. public class GlobalException {
  8. @ExceptionHandler(ServiceException.class)
  9. @ResponseBody
  10. public Result serviceException(ServiceException e) {
  11. return Result.error(e.getCode(), e.getMessage());
  12. }
  13. @ExceptionHandler(Exception.class)
  14. @ResponseBody
  15. public Result globalException(Exception e) {
  16. e.printStackTrace();
  17. return Result.error("500", "系统错误");
  18. }
  19. }
  20. package com.example.springboot.exception;
  21. import lombok.Getter;
  22. @Getter
  23. public class ServiceException extends RuntimeException {
  24. private final String code;
  25. public ServiceException(String msg) {
  26. super(msg);
  27. this.code = "500";
  28. }
  29. public ServiceException(String code, String msg) {
  30. super(msg);
  31. this.code = code;
  32. }
  33. }

发表评论

表情:
评论列表 (有 0 条评论,135人围观)

还没有评论,来说两句吧...

相关阅读

    相关 js登录注册验证

    经常写js验证,感觉很麻烦,这次就把他整理贴出来,以后可以直接用了。具体介绍这里不罗嗦了,直接贴代码,相信大家都能理解代码的含义 登录验证: Jsp页面代码: <