首先需要读者已经具备基本了解vue2.X和springboot2.X,element ui等技术
首先是登录页面的写法:
<template> <div class="login_container"> <div class="login_box"> <!-- 头像区域 --> <div class="avatar_box"> <img src="../assets/logo.png" alt=""> </div> <!-- 登录表单区域 --> <el-form ref="loginFormRef" :model="loginForm" :rules="loginFormRules" label-width="0px" class="login_form"> <!-- 用户名 --> <el-form-item prop="username"> <el-input v-model="loginForm.username" prefix-icon="iconfont icon-user"></el-input> </el-form-item> <!-- 密码 --> <el-form-item prop="password"> <el-input v-model="loginForm.password" prefix-icon="iconfont icon-3702mima" type="password"></el-input> </el-form-item> <!-- 按钮区域 --> <el-form-item class="btns"> <el-button type="primary" @click="login">登录</el-button> <el-button type="info" @click="resetLoginForm">重置</el-button> </el-form-item> </el-form> </div> </div> </template> <script> export default { name: 'Login', data() { return { // 这是登录表单的数据绑定对象 loginForm: { username: 'admin', password: '123456' }, // 这是表单的验证规则对象 loginFormRules: { // 验证用户名是否合法 username: [ { required: true, message: '请输入登录名称', trigger: 'blur' }, { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' } ], // 验证密码是否合法 password: [ { required: true, message: '请输入登录密码', trigger: 'blur' }, { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' } ] } } }, methods:{ // 点击重置按钮,重置登录表单 resetLoginForm() { // console.log(this); this.$refs.loginFormRef.resetFields()//重置 }, login() { this.$refs.loginFormRef.validate(valid => { if (!valid) return; console.log('loginForm的值:',this.loginForm); this.$myAxios.post('api/login/loginIn',this.loginForm) .then(res=>{ console.log('res的值:',res); }); /*if (res.meta.status !== 200) return this.$message.error('登录失败!') this.$message.success('登录成功') // 1. 将登录成功之后的 token,保存到客户端的 sessionStorage 中 // 1.1 项目中出了登录之外的其他API接口,必须在登录之后才能访问 // 1.2 token 只应在当前网站打开期间生效,所以将 token 保存在 sessionStorage 中 window.sessionStorage.setItem('token', res.data.token) // 2. 通过编程式导航跳转到后台主页,路由地址是 /home this.$router.push('/home')*/ }) } } } </script> <!--scoped防止各个组件之间样式冲突--> <style scoped> .login_container { background-color: #2b4b6b; height: 100%; } .login_box { width: 450px; height: 300px; background-color: #fff; border-radius: 3px; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } .avatar_box { height: 130px; width: 130px; border: 1px solid #eee; border-radius: 50%; padding: 10px; box-shadow: 0 0 10px #ddd; position: absolute; left: 50%; transform: translate(-50%, -50%); background-color: #fff; } img { width: 100%; height: 100%; border-radius: 50%; background-color: #eee; } .login_form { position: absolute; bottom: 0; width: 100%; padding: 0 20px; box-sizing: border-box; } .btns { display: flex; justify-content: flex-end; } </style>
对以上代码关键地方进行说明,首先读者需要自己解决掉跨域问题,然后登录表单绑定loginForm,并将输入的账号和密码也绑定到loginForm上,在data()中定义loginForm,这样当用户输入账号和密码时,就可以自动绑定到loginForm中。登录采用axios的post请求,利用res返回在控制台上打印输出结果,来判断是否获取到数据。
下面是springboot的关键代码部分
package com.smp.controller; import com.smp.model.LoginForm; import com.smp.model.VUser; import com.smp.utils.JwtUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("api/login") @Slf4j public class LoginController { @GetMapping("/hello") public String hello() { return "hello"; } @PostMapping("loginIn") public String loginIn(@RequestBody LoginForm loginForm){ log.info("运行到loginIn方法来了................."); log.info("loginForm的值:",loginForm.toString()); log.info("username的值:"+loginForm.getUsername()); log.info("password的值:"+loginForm.getPassword()); if(loginForm.getUsername()==null||loginForm.getPassword()==null) return null; if(loginForm.getUsername().equals("admin")&&loginForm.getPassword().equals("123456")){ VUser vUser=new VUser(); vUser.setId(500); vUser.setId(0); vUser.setUsername("admin"); vUser.setMobile("123"); vUser.setEmail("123@qq.com"); return JwtUtils.getJsonWebToken(vUser); } return null; } }
返回的jwt格式的token到前端,在springboot中定义一个LoginForm对象,里面只有账号和密码两个属性,跟前端的login.vue里面的保持一致。需要注意的是,需要使用@requestbody注解,否则无法获取到前端传过来的数据
下面是一些可以参考的代码
LoginForm类
package com.smp.model; import lombok.Data; import java.io.Serializable; @Data public class LoginForm implements Serializable { private String username; private String password; }
vue.config.js
module.exports = { // 将部署应用程序的基本URL // 将部署应用程序的基本URL。 // 默认情况下,Vue CLI假设您的应用程序将部署在域的根目录下。 // https://www.my-app.com/。如果应用程序部署在子路径上,则需要使用此选项指定子路径。例如,如果您的应用程序部署在https://www.foobar.com/my-app/,集baseUrl到'/my-app/'. publicPath: process.env.NODE_ENV === 'production' ? './' : './', // outputDir: 在npm run build时 生成文件的目录 type:string, default:'dist' outputDir: 'dist', // pages:{ type:Object,Default:undfind } /* 构建多页面模式的应用程序.每个“页面”都应该有一个相应的JavaScript条目文件。该值应该是一 个对象,其中键是条目的名称,而该值要么是指定其条目、模板和文件名的对象,要么是指定其条目 的字符串, 注意:请保证pages里配置的路径和文件名 在你的文档目录都存在 否则启动服务会报错的 */ pages: { index: { //entry for the page entry: 'src/main.js', //the source template template: 'public/index.html', //output as dist/index.html filename: 'index.html' }, // when using the entry-only string format, // template is inferred to be `public/subpage.html` // and falls back to `public/index.html` if not found. // Output filename is inferred to be `subpage.html`. // subpage: 'src/subpage/main.js' }, // lintOnSave:{ type:Boolean default:true } 问你是否使用eslint lintOnSave: true, // productionSourceMap:{ type:Bollean,default:true } 生产源映射 // 如果您不需要生产时的源映射,那么将此设置为false可以加速生产构建 productionSourceMap: false, // devServer:{type:Object} 3个属性host,port,https // 它支持webPack-dev-server的所有选项 devServer: { port: 8080, // 端口号 host: 'localhost', https: false, // https:{type:Boolean} open: true, //配置自动启动浏览器 proxy: {// 配置跨域处理,只有一个代理 "/api": { target: "http://localhost:8082/api", // 要访问的接口域名 ws: true, // 是否启用websockets changeOrigin: true, //开启代理:在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题 pathRewrite: { '^/api': "/" //这里理解成用'/api'代替target里面的地址,比如我要调用'http://40.00.100.100:3002/user/add',直接写'/api/user/add'即可 } } } /*css: { loaderOptions: { css: {}, postcss: { plugins: [ /!*require('postcss-px2rem')({ remUnit: 37.5 })*!/ ] } } }*/ } };
import axios from 'axios'; import { Message,Loading } from 'element-ui'; //import router from '../router/router';当判断登录失效时跳转用的 //载入动画配置 let loading; function startLoading() { loading=Loading.service({ lock:true, text:'拼命加载中.......', background:'rgba(0,0,0,0,7)' }); } function endLoading() { loading.close(); } //以下为对axios的配置 axios.defaults.timeout = 5000; //超时终止请求 //axios.defaults.baseURL ='http://localhost:8080/'; //配置请求地址 //axios.defaults.withCredentials = true; /* const instance = axios.create({ headers: { 'content-type': 'application/json;charset=UTF-8' // 'token': 'one' }, // baseURL: 'https://easy-mock.com/mock/5c01e1f6f221b94c907213d6/', timeout: 10000, // withCredentials: true });*/ //request请求拦截 axios.interceptors.request.use( config=>{ //加载动画 startLoading(); /* if(localStorage.eleToken){ //设置统一的请求头 config.headers.Authorization=localStorage.eleToken; }*/ config.data=JSON.stringify(config.data); config.headers={ 'Content-Type':'application/json;charset=UTF-8' }; return config; }, error => { return Promise.reject(error); } ); //response响应拦截 axios.interceptors.response.use( response=>{ //结束加载动画 endLoading(); return response; }, error => { //错误提醒 endLoading(); Message.error(error.response.data); //获取错误状态码 const status=error.response; if(status===401){ Message.error('token失效,请重新登录'); //清除token // localStorage.removeItem('eleToken'); //跳转到登录页面 // router.push('/login'); } return Promise.reject(error); } ); //为模块指定默认输出 export default axios;