效果图
请求流程
1. 客户端点击按钮发起请求
2.请求拦截器拦截并处理请求
3.服务器收到请求,处理后返回相应的状态和数据
4.响应拦截器拦截并处理请求
5.如果res.data.code === 200,提示成功信息,返回数据,接着执行.then(res =>{…});否则提示错误信息,不往下执行。
vue目录结构
package.json
...
"dependencies": {
"core-js": "^2.6.5",
"axios": "0.21.0",
"element-ui": "^2.15.1",
"jsencrypt": "^3.1.0",
"vue": "^2.6.10"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.0.4",
"@vue/cli-plugin-eslint": "^3.0.4",
"@vue/cli-service": "^3.0.4",
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"sass": "1.32.0",
"sass-loader": "10.1.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.6.10"
},
...
main.js
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
App.vue
<template>
<div id="app">
<login />
</div>
</template>
<script>
import Login from '@/views/login'
export default {
name: 'app',
components: {
Login
}
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
/*撑开内容充满整个页面*/
#app {
position: absolute;
width: 100%;
top: 0;
bottom: 0;
}
</style>
request.js(配置axios)
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
// 设置请求头
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const http = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: 'http://localhost:8080/',
// 超时
timeout: 10 * 1000
})
// 请求拦截器
http.interceptors.request.use(config => {
let token = Cookies.get(TokenKey);
console.log(token)
if (token) {
// 让每个请求携带自定义token 请根据实际情况自行修改
config.headers['Authorization'] = 'Bearer ' + token;
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?';
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && typeof(value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
url += subPart + encodeURIComponent(value[key]) + "&";
}
} else {
url += part + encodeURIComponent(value) + "&";
}
}
}
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
http.interceptors.response.use(res => {
const code = res.data.code;
const msg = res.data.msg;
if (code === 200) {
Cookies.set(TokenKey, res.data.tokenKey);
Message.success(msg);
return res.data;
} else if (code === 401) {
MessageBox.confirm('登录状态已过期,请您重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
Cookies.remove(TokenKey);
location.href = '/login';
})
} else {
Message.error(msg);
return Promise.reject(msg)
}
},
error => {
console.log("" + error)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
}
else if (message.includes("timeout")) {
message = "系统接口请求超时";
}
else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message.error(message)
return Promise.reject(error)
}
)
export default http
login.js
import http from '@/utils/request'
// 保存登录信息
export function insertLoginInfo(params) { return http.post('/login', params) }
//获取登录信息
export function getLoginInfo(params) { return http.get('/login', {params}) }
login.vue
<template>
<div class="login">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">账号密码登录</h3>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号">
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
auto-complete="off"
placeholder="密码"
@keyup.enter.native="handleLogin"
>
</el-input>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
<el-form-item>
<el-row>
<el-col :span="8">
<el-button
:loading="loading"
size="medium"
type="primary"
@click.native.prevent="handleLogin"
>
<span v-if="!loading">get</span>
<span v-else>loading...</span>
</el-button>
</el-col>
<el-col :span="8">
<el-button
:loading="loading"
size="medium"
type="primary"
@click.native.prevent="handleLogin2"
>
<span v-if="!loading">post</span>
<span v-else>loading...</span>
</el-button>
</el-col>
</el-row>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2021 All Rights Reserved.</span>
</div>
</div>
</template>
<script>
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'
import { insertLoginInfo, getLoginInfo } from "@/api/login";
export default {
name: "Login",
data() {
return {
cookiePassword: "",
loginForm: {
username: "admin",
password: "admin",
rememberMe: false,
},
loginRules: {
username: [{ required: true, trigger: "blur", message: "用户名不能为空" }],
password: [{ required: true, trigger: "blur", message: "密码不能为空" }]
},
loading: false,
};
},
created() {
this.getCookie();
},
methods: {
getCookie() {
const username = Cookies.get("username");
const password = Cookies.get("password");
const rememberMe = Cookies.get('rememberMe')
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
};
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
if (this.loginForm.rememberMe) {
Cookies.set("username", this.loginForm.username, { expires: 30 });
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
} else {
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove('rememberMe');
}
getLoginInfo(this.loginForm).then(()=>{
this.loading = false;
})
}
});
},
handleLogin2() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true;
if (this.loginForm.rememberMe) {
Cookies.set("username", this.loginForm.username, { expires: 30 });
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
} else {
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove('rememberMe');
}
insertLoginInfo(this.loginForm).then(()=>{
this.loading = false;
})
}
});
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background-image: url("../assets/images/login-background.png");
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
}
.login-form {
border-radius: 6px;
background: #ffffff;
width: 300px;
padding: 25px 25px 5px 25px;
.el-input {
height: 38px;
input {
height: 38px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 2px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 38px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
</style>
jsencrypt.js
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
// 密钥对生成 http://web.chacuo.net/netrsakeypair
const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
'7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' +
'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' +
'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' +
'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
'UP8iWi1Qw0Y='
// 加密
export function encrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(publicKey) // 设置公钥
return encryptor.encrypt(txt) // 对数据进行加密
}
// 解密
export function decrypt(txt) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(privateKey) // 设置私钥
return encryptor.decrypt(txt) // 对数据进行解密
}
springboot目录结构
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.13.RELEASE</version>
</dependency>
主启动类
package com.yxtycs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author yuxt
* @date 2021/4/17
*/
@SpringBootApplication
public class WebApplication2 {
public static void main(String[] args) {
SpringApplication.run(WebApplication2.class, args);
}
}
LoginController.java
package com.yxtycs.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.UUID;
/**
* @author yuxt
* @date 2021/4/17
*/
@Controller
public class LoginController {
protected final Logger logger = LoggerFactory.getLogger(LoginController.class);
enum resInfo {
suc200(200, "操作成功!"), err400(400, "操作失败!"),
err401(401, "权限认证失败!"), err500(500, "服务器错误!"),
err555(555, "未知错误!请联系管理员!");
private final int code;
private final String msg;
private resInfo(int code, String msg) {
this.code = code;
this.msg = msg;
}
}
private HashMap<String, Object> map = new HashMap<>();
private int n = 0;
@PostMapping("/login")
@ResponseBody
public HashMap<String, Object> insertLoginInfo(@RequestBody HashMap<String, Object> params, HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
logger.info("get token:" + authorization);
n = n % 5;
switch (n) {
case 0:
map.put("code", resInfo.suc200.code);
map.put("msg", resInfo.suc200.msg);
break;
case 1:
map.put("code", resInfo.err400.code);
map.put("msg", resInfo.err400.msg);
break;
case 2:
map.put("code", resInfo.err401.code);
map.put("msg", resInfo.err401.msg);
break;
case 3:
map.put("code", resInfo.err500.code);
map.put("msg", resInfo.err500.msg);
break;
default:
map.put("code", resInfo.err555.code);
map.put("msg", resInfo.err555.msg);
}
return map;
}
@GetMapping("/login")
@ResponseBody
public HashMap<String, Object> getLoginInfo(@RequestParam HashMap<String, Object> params) {
HashMap<String, Object> map = new HashMap<>();
n = n % 5;
switch (n) {
case 0:
map.put("code", resInfo.suc200.code);
map.put("msg", resInfo.suc200.msg);
break;
case 1:
map.put("code", resInfo.err400.code);
map.put("msg", resInfo.err400.msg);
break;
case 2:
map.put("code", resInfo.err401.code);
map.put("msg", resInfo.err401.msg);
break;
case 3:
map.put("code", resInfo.err500.code);
map.put("msg", resInfo.err500.msg);
break;
default:
map.put("code", resInfo.err555.code);
map.put("msg", resInfo.err555.msg);
}
n++;
String token = UUID.randomUUID().toString();
logger.info("set token:" + token);
map.put("tokenKey", token);
return map;
}
}
启动项目,前端报跨域请求错误
xxx from origin xxx has been blocked by CORS policy: Response to preflight request doesn’t pass
解决方式:参考文章@CrossOrigin详解
或者编写 MvcConfig.java
package com.yxtycs.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author yuxt
* @date 2021/4/17
*/
@Configuration
public class MvcConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}