RSA加密登录时账号和密码
This commit is contained in:
parent
612c4293d1
commit
94c81296d0
@ -1,7 +1,15 @@
|
|||||||
package com.ruoyi.web.controller.system;
|
package com.ruoyi.web.controller.system;
|
||||||
|
|
||||||
|
import java.security.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.DecryptLogin;
|
||||||
|
import com.ruoyi.common.annotation.RateLimiter;
|
||||||
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
|
import com.ruoyi.common.enums.LimitType;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -34,12 +42,33 @@ public class SysLoginController
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SysPermissionService permissionService;
|
private SysPermissionService permissionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录前先生成一个RSA密钥,LoginBody中的账号密码进行加密。
|
||||||
|
* 公钥返回给前端加密使用;
|
||||||
|
* 私钥存入redis,等待登陆时解密使用。
|
||||||
|
*
|
||||||
|
* 这个接口需要加入白名单,所以使用限流器 RateLimiter 对IP限制每秒请求次数
|
||||||
|
* @return public key
|
||||||
|
*/
|
||||||
|
@RateLimiter(time = 1, count = 5, limitType = LimitType.IP)
|
||||||
|
@GetMapping("/preLogin")
|
||||||
|
public AjaxResult preLogin() {
|
||||||
|
String publicKey;
|
||||||
|
try {
|
||||||
|
publicKey = loginService.generateRSA();
|
||||||
|
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||||
|
return AjaxResult.error("生成RSA密钥对失败");
|
||||||
|
}
|
||||||
|
return AjaxResult.success("", publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录方法
|
* 登录方法
|
||||||
*
|
*
|
||||||
* @param loginBody 登录信息
|
* @param loginBody 登录信息
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
|
@DecryptLogin
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
public AjaxResult login(@RequestBody LoginBody loginBody)
|
public AjaxResult login(@RequestBody LoginBody loginBody)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.ruoyi.common.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA解密注解
|
||||||
|
* 使用该注解,可以对已经加密的参数进行解密
|
||||||
|
* @author wrw
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface DecryptLogin {
|
||||||
|
}
|
@ -39,6 +39,11 @@ public class Constants
|
|||||||
*/
|
*/
|
||||||
public static final String FAIL = "1";
|
public static final String FAIL = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预登录 redis key
|
||||||
|
*/
|
||||||
|
public static final String PRE_LOGIN_KEY = "pre_login_key:";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录成功
|
* 登录成功
|
||||||
*/
|
*/
|
||||||
|
@ -27,6 +27,11 @@ public class LoginBody
|
|||||||
*/
|
*/
|
||||||
private String uuid = "";
|
private String uuid = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA公钥
|
||||||
|
*/
|
||||||
|
private String publicKey;
|
||||||
|
|
||||||
public String getUsername()
|
public String getUsername()
|
||||||
{
|
{
|
||||||
return username;
|
return username;
|
||||||
@ -66,4 +71,8 @@ public class LoginBody
|
|||||||
{
|
{
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPublicKey() {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.ruoyi.framework.aspectj;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.DecryptLogin;
|
||||||
|
import com.ruoyi.common.constant.Constants;
|
||||||
|
import com.ruoyi.common.core.domain.model.LoginBody;
|
||||||
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
|
import com.ruoyi.common.exception.user.UserException;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.aspectj.lang.JoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Before;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RSA解密
|
||||||
|
*
|
||||||
|
* @author wrw
|
||||||
|
*/
|
||||||
|
@Aspect
|
||||||
|
@Component
|
||||||
|
public class DecryptParameters {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
|
||||||
|
@Before("@annotation(decrypt)")
|
||||||
|
public void doBefore(JoinPoint point, DecryptLogin decrypt) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException {
|
||||||
|
LoginBody pointArg = (LoginBody) point.getArgs()[0];
|
||||||
|
//从缓存里获取私钥
|
||||||
|
String loginPrivateKey = redisCache.getCacheObject(Constants.PRE_LOGIN_KEY + pointArg.getPublicKey());
|
||||||
|
if (StringUtils.isEmpty(loginPrivateKey)) {
|
||||||
|
throw new UserException("RSA密钥对已过期!", point.getArgs());
|
||||||
|
}
|
||||||
|
//初始化解密密钥
|
||||||
|
PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(loginPrivateKey));
|
||||||
|
//创建密钥工厂
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
//解析密钥
|
||||||
|
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
|
||||||
|
Cipher cipher = Cipher.getInstance("RSA");
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||||
|
//解密
|
||||||
|
byte[] username = cipher.doFinal(Base64.decodeBase64(pointArg.getUsername()));
|
||||||
|
byte[] password = cipher.doFinal(Base64.decodeBase64(pointArg.getPassword()));
|
||||||
|
pointArg.setUsername(new String(username));
|
||||||
|
pointArg.setPassword(new String(password));
|
||||||
|
}
|
||||||
|
}
|
@ -97,7 +97,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
|
|||||||
// 过滤请求
|
// 过滤请求
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||||
.antMatchers("/login", "/register", "/captchaImage").anonymous()
|
.antMatchers("/preLogin", "/login", "/register", "/captchaImage").anonymous()
|
||||||
.antMatchers(
|
.antMatchers(
|
||||||
HttpMethod.GET,
|
HttpMethod.GET,
|
||||||
"/",
|
"/",
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.ruoyi.framework.web.service;
|
package com.ruoyi.framework.web.service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
@ -24,6 +26,9 @@ import com.ruoyi.framework.manager.factory.AsyncFactory;
|
|||||||
import com.ruoyi.system.service.ISysConfigService;
|
import com.ruoyi.system.service.ISysConfigService;
|
||||||
import com.ruoyi.system.service.ISysUserService;
|
import com.ruoyi.system.service.ISysUserService;
|
||||||
|
|
||||||
|
import java.security.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录校验方法
|
* 登录校验方法
|
||||||
*
|
*
|
||||||
@ -130,4 +135,25 @@ public class SysLoginService
|
|||||||
sysUser.setLoginDate(DateUtils.getNowDate());
|
sysUser.setLoginDate(DateUtils.getNowDate());
|
||||||
userService.updateUserProfile(sysUser);
|
userService.updateUserProfile(sysUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成RSA密钥对
|
||||||
|
* @return 公钥
|
||||||
|
*/
|
||||||
|
public String generateRSA() throws NoSuchAlgorithmException, NoSuchProviderException {
|
||||||
|
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA", "SunRsaSign");
|
||||||
|
gen.initialize(512, new SecureRandom());
|
||||||
|
KeyPair pair = gen.generateKeyPair();
|
||||||
|
|
||||||
|
byte[] privateKey = pair.getPrivate().getEncoded();
|
||||||
|
byte[] publicKey = pair.getPublic().getEncoded();
|
||||||
|
|
||||||
|
privateKey = Base64.encodeBase64(privateKey);
|
||||||
|
publicKey = Base64.encodeBase64(publicKey);
|
||||||
|
|
||||||
|
//私钥存入缓存
|
||||||
|
redisCache.setCacheObject(Constants.PRE_LOGIN_KEY + new String(publicKey), new String(privateKey), 30, TimeUnit.SECONDS);
|
||||||
|
//返回公钥
|
||||||
|
return new String(publicKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,75 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 登录方法
|
// 预登陆,获取RSA密钥
|
||||||
export function login(username, password, code, uuid) {
|
export function preLogin() {
|
||||||
const data = {
|
return new Promise(resolve => {
|
||||||
username,
|
request({
|
||||||
password,
|
url: '/preLogin',
|
||||||
code,
|
headers: {
|
||||||
uuid
|
isToken: false
|
||||||
}
|
},
|
||||||
return request({
|
method: 'get'
|
||||||
url: '/login',
|
}).then(res =>{
|
||||||
headers: {
|
resolve(res.data)
|
||||||
isToken: false
|
})
|
||||||
},
|
})
|
||||||
method: 'post',
|
}
|
||||||
data: data
|
|
||||||
})
|
// 登录方法
|
||||||
}
|
export function login(username, password, code, uuid, publicKey) {
|
||||||
|
const data = {
|
||||||
// 注册方法
|
username,
|
||||||
export function register(data) {
|
password,
|
||||||
return request({
|
code,
|
||||||
url: '/register',
|
uuid,
|
||||||
headers: {
|
publicKey
|
||||||
isToken: false
|
}
|
||||||
},
|
return request({
|
||||||
method: 'post',
|
url: '/login',
|
||||||
data: data
|
headers: {
|
||||||
})
|
isToken: false
|
||||||
}
|
},
|
||||||
|
method: 'post',
|
||||||
// 获取用户详细信息
|
data: data
|
||||||
export function getInfo() {
|
})
|
||||||
return request({
|
}
|
||||||
url: '/getInfo',
|
|
||||||
method: 'get'
|
// 注册方法
|
||||||
})
|
export function register(data) {
|
||||||
}
|
return request({
|
||||||
|
url: '/register',
|
||||||
// 退出方法
|
headers: {
|
||||||
export function logout() {
|
isToken: false
|
||||||
return request({
|
},
|
||||||
url: '/logout',
|
method: 'post',
|
||||||
method: 'post'
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取验证码
|
// 获取用户详细信息
|
||||||
export function getCodeImg() {
|
export function getInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/captchaImage',
|
url: '/getInfo',
|
||||||
headers: {
|
method: 'get'
|
||||||
isToken: false
|
})
|
||||||
},
|
}
|
||||||
method: 'get',
|
|
||||||
timeout: 20000
|
// 退出方法
|
||||||
})
|
export function logout() {
|
||||||
}
|
return request({
|
||||||
|
url: '/logout',
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取验证码
|
||||||
|
export function getCodeImg() {
|
||||||
|
return request({
|
||||||
|
url: '/captchaImage',
|
||||||
|
headers: {
|
||||||
|
isToken: false
|
||||||
|
},
|
||||||
|
method: 'get',
|
||||||
|
timeout: 20000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,96 +1,97 @@
|
|||||||
import { login, logout, getInfo } from '@/api/login'
|
import { login, logout, getInfo } from '@/api/login'
|
||||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
state: {
|
state: {
|
||||||
token: getToken(),
|
token: getToken(),
|
||||||
name: '',
|
name: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
roles: [],
|
roles: [],
|
||||||
permissions: []
|
permissions: []
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
SET_TOKEN: (state, token) => {
|
SET_TOKEN: (state, token) => {
|
||||||
state.token = token
|
state.token = token
|
||||||
},
|
},
|
||||||
SET_NAME: (state, name) => {
|
SET_NAME: (state, name) => {
|
||||||
state.name = name
|
state.name = name
|
||||||
},
|
},
|
||||||
SET_AVATAR: (state, avatar) => {
|
SET_AVATAR: (state, avatar) => {
|
||||||
state.avatar = avatar
|
state.avatar = avatar
|
||||||
},
|
},
|
||||||
SET_ROLES: (state, roles) => {
|
SET_ROLES: (state, roles) => {
|
||||||
state.roles = roles
|
state.roles = roles
|
||||||
},
|
},
|
||||||
SET_PERMISSIONS: (state, permissions) => {
|
SET_PERMISSIONS: (state, permissions) => {
|
||||||
state.permissions = permissions
|
state.permissions = permissions
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
// 登录
|
// 登录
|
||||||
Login({ commit }, userInfo) {
|
Login({ commit }, userInfo) {
|
||||||
const username = userInfo.username.trim()
|
const username = userInfo.username.trim()
|
||||||
const password = userInfo.password
|
const password = userInfo.password
|
||||||
const code = userInfo.code
|
const code = userInfo.code
|
||||||
const uuid = userInfo.uuid
|
const uuid = userInfo.uuid
|
||||||
return new Promise((resolve, reject) => {
|
const publicKey = userInfo.publicKey
|
||||||
login(username, password, code, uuid).then(res => {
|
return new Promise((resolve, reject) => {
|
||||||
setToken(res.token)
|
login(username, password, code, uuid, publicKey).then(res => {
|
||||||
commit('SET_TOKEN', res.token)
|
setToken(res.token)
|
||||||
resolve()
|
commit('SET_TOKEN', res.token)
|
||||||
}).catch(error => {
|
resolve()
|
||||||
reject(error)
|
}).catch(error => {
|
||||||
})
|
reject(error)
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
},
|
||||||
// 获取用户信息
|
|
||||||
GetInfo({ commit, state }) {
|
// 获取用户信息
|
||||||
return new Promise((resolve, reject) => {
|
GetInfo({ commit, state }) {
|
||||||
getInfo().then(res => {
|
return new Promise((resolve, reject) => {
|
||||||
const user = res.user
|
getInfo().then(res => {
|
||||||
const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
|
const user = res.user
|
||||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
|
||||||
commit('SET_ROLES', res.roles)
|
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||||
commit('SET_PERMISSIONS', res.permissions)
|
commit('SET_ROLES', res.roles)
|
||||||
} else {
|
commit('SET_PERMISSIONS', res.permissions)
|
||||||
commit('SET_ROLES', ['ROLE_DEFAULT'])
|
} else {
|
||||||
}
|
commit('SET_ROLES', ['ROLE_DEFAULT'])
|
||||||
commit('SET_NAME', user.userName)
|
}
|
||||||
commit('SET_AVATAR', avatar)
|
commit('SET_NAME', user.userName)
|
||||||
resolve(res)
|
commit('SET_AVATAR', avatar)
|
||||||
}).catch(error => {
|
resolve(res)
|
||||||
reject(error)
|
}).catch(error => {
|
||||||
})
|
reject(error)
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
},
|
||||||
// 退出系统
|
|
||||||
LogOut({ commit, state }) {
|
// 退出系统
|
||||||
return new Promise((resolve, reject) => {
|
LogOut({ commit, state }) {
|
||||||
logout(state.token).then(() => {
|
return new Promise((resolve, reject) => {
|
||||||
commit('SET_TOKEN', '')
|
logout(state.token).then(() => {
|
||||||
commit('SET_ROLES', [])
|
commit('SET_TOKEN', '')
|
||||||
commit('SET_PERMISSIONS', [])
|
commit('SET_ROLES', [])
|
||||||
removeToken()
|
commit('SET_PERMISSIONS', [])
|
||||||
resolve()
|
removeToken()
|
||||||
}).catch(error => {
|
resolve()
|
||||||
reject(error)
|
}).catch(error => {
|
||||||
})
|
reject(error)
|
||||||
})
|
})
|
||||||
},
|
})
|
||||||
|
},
|
||||||
// 前端 登出
|
|
||||||
FedLogOut({ commit }) {
|
// 前端 登出
|
||||||
return new Promise(resolve => {
|
FedLogOut({ commit }) {
|
||||||
commit('SET_TOKEN', '')
|
return new Promise(resolve => {
|
||||||
removeToken()
|
commit('SET_TOKEN', '')
|
||||||
resolve()
|
removeToken()
|
||||||
})
|
resolve()
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export default user
|
|
||||||
|
export default user
|
||||||
|
@ -28,3 +28,15 @@ export function decrypt(txt) {
|
|||||||
return encryptor.decrypt(txt) // 对数据进行解密
|
return encryptor.decrypt(txt) // 对数据进行解密
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对登录的用户名密码加密
|
||||||
|
* @param loginPublicKey
|
||||||
|
* @param txt 待加密文本
|
||||||
|
* @returns {Promise<ArrayBuffer>}
|
||||||
|
*/
|
||||||
|
export function encryptLogin(loginPublicKey, txt) {
|
||||||
|
const encrypt = new JSEncrypt();
|
||||||
|
encrypt.setPublicKey(loginPublicKey);
|
||||||
|
return encrypt.encrypt(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,219 +1,229 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
|
||||||
<h3 class="title">若依后台管理系统</h3>
|
<h3 class="title">若依后台管理系统</h3>
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="loginForm.username"
|
v-model="loginForm.username"
|
||||||
type="text"
|
type="text"
|
||||||
auto-complete="off"
|
auto-complete="off"
|
||||||
placeholder="账号"
|
placeholder="账号"
|
||||||
>
|
>
|
||||||
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
|
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="password">
|
<el-form-item prop="password">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="loginForm.password"
|
v-model="loginForm.password"
|
||||||
type="password"
|
type="password"
|
||||||
auto-complete="off"
|
auto-complete="off"
|
||||||
placeholder="密码"
|
placeholder="密码"
|
||||||
@keyup.enter.native="handleLogin"
|
@keyup.enter.native="handleLogin"
|
||||||
>
|
>
|
||||||
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
|
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="code" v-if="captchaOnOff">
|
<el-form-item prop="code" v-if="captchaOnOff">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="loginForm.code"
|
v-model="loginForm.code"
|
||||||
auto-complete="off"
|
auto-complete="off"
|
||||||
placeholder="验证码"
|
placeholder="验证码"
|
||||||
style="width: 63%"
|
style="width: 63%"
|
||||||
@keyup.enter.native="handleLogin"
|
@keyup.enter.native="handleLogin"
|
||||||
>
|
>
|
||||||
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
|
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
|
||||||
</el-input>
|
</el-input>
|
||||||
<div class="login-code">
|
<div class="login-code">
|
||||||
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
<el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox>
|
||||||
<el-form-item style="width:100%;">
|
<el-form-item style="width:100%;">
|
||||||
<el-button
|
<el-button
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
size="medium"
|
size="medium"
|
||||||
type="primary"
|
type="primary"
|
||||||
style="width:100%;"
|
style="width:100%;"
|
||||||
@click.native.prevent="handleLogin"
|
@click.native.prevent="handleLogin"
|
||||||
>
|
>
|
||||||
<span v-if="!loading">登 录</span>
|
<span v-if="!loading">登 录</span>
|
||||||
<span v-else>登 录 中...</span>
|
<span v-else>登 录 中...</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
<div style="float: right;" v-if="register">
|
<div style="float: right;" v-if="register">
|
||||||
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
<router-link class="link-type" :to="'/register'">立即注册</router-link>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<!-- 底部 -->
|
<!-- 底部 -->
|
||||||
<div class="el-login-footer">
|
<div class="el-login-footer">
|
||||||
<span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span>
|
<span>Copyright © 2018-2022 ruoyi.vip All Rights Reserved.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getCodeImg } from "@/api/login";
|
import { preLogin } from "@/api/login";
|
||||||
import Cookies from "js-cookie";
|
import { getCodeImg } from "@/api/login";
|
||||||
import { encrypt, decrypt } from '@/utils/jsencrypt'
|
import Cookies from "js-cookie";
|
||||||
|
import { encrypt, decrypt, encryptLogin } from '@/utils/jsencrypt'
|
||||||
export default {
|
|
||||||
name: "Login",
|
export default {
|
||||||
data() {
|
name: "Login",
|
||||||
return {
|
data() {
|
||||||
codeUrl: "",
|
return {
|
||||||
loginForm: {
|
codeUrl: "",
|
||||||
username: "admin",
|
loginForm: {
|
||||||
password: "admin123",
|
username: "admin",
|
||||||
rememberMe: false,
|
password: "admin123",
|
||||||
code: "",
|
rememberMe: false,
|
||||||
uuid: ""
|
code: "",
|
||||||
},
|
uuid: ""
|
||||||
loginRules: {
|
},
|
||||||
username: [
|
loginRules: {
|
||||||
{ required: true, trigger: "blur", message: "请输入您的账号" }
|
username: [
|
||||||
],
|
{ required: true, trigger: "blur", message: "请输入您的账号" }
|
||||||
password: [
|
],
|
||||||
{ required: true, trigger: "blur", message: "请输入您的密码" }
|
password: [
|
||||||
],
|
{ required: true, trigger: "blur", message: "请输入您的密码" }
|
||||||
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
],
|
||||||
},
|
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
|
||||||
loading: false,
|
},
|
||||||
// 验证码开关
|
loading: false,
|
||||||
captchaOnOff: true,
|
// 验证码开关
|
||||||
// 注册开关
|
captchaOnOff: true,
|
||||||
register: false,
|
// 注册开关
|
||||||
redirect: undefined
|
register: false,
|
||||||
};
|
redirect: undefined
|
||||||
},
|
};
|
||||||
watch: {
|
},
|
||||||
$route: {
|
watch: {
|
||||||
handler: function(route) {
|
$route: {
|
||||||
this.redirect = route.query && route.query.redirect;
|
handler: function(route) {
|
||||||
},
|
this.redirect = route.query && route.query.redirect;
|
||||||
immediate: true
|
},
|
||||||
}
|
immediate: true
|
||||||
},
|
}
|
||||||
created() {
|
},
|
||||||
this.getCode();
|
created() {
|
||||||
this.getCookie();
|
this.getCode();
|
||||||
},
|
this.getCookie();
|
||||||
methods: {
|
},
|
||||||
getCode() {
|
methods: {
|
||||||
getCodeImg().then(res => {
|
getCode() {
|
||||||
this.captchaOnOff = res.captchaOnOff === undefined ? true : res.captchaOnOff;
|
getCodeImg().then(res => {
|
||||||
if (this.captchaOnOff) {
|
this.captchaOnOff = res.captchaOnOff === undefined ? true : res.captchaOnOff;
|
||||||
this.codeUrl = "data:image/gif;base64," + res.img;
|
if (this.captchaOnOff) {
|
||||||
this.loginForm.uuid = res.uuid;
|
this.codeUrl = "data:image/gif;base64," + res.img;
|
||||||
}
|
this.loginForm.uuid = res.uuid;
|
||||||
});
|
}
|
||||||
},
|
});
|
||||||
getCookie() {
|
},
|
||||||
const username = Cookies.get("username");
|
getCookie() {
|
||||||
const password = Cookies.get("password");
|
const username = Cookies.get("username");
|
||||||
const rememberMe = Cookies.get('rememberMe')
|
const password = Cookies.get("password");
|
||||||
this.loginForm = {
|
const rememberMe = Cookies.get('rememberMe')
|
||||||
username: username === undefined ? this.loginForm.username : username,
|
this.loginForm = {
|
||||||
password: password === undefined ? this.loginForm.password : decrypt(password),
|
username: username === undefined ? this.loginForm.username : username,
|
||||||
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
|
password: password === undefined ? this.loginForm.password : decrypt(password),
|
||||||
};
|
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
|
||||||
},
|
};
|
||||||
handleLogin() {
|
},
|
||||||
this.$refs.loginForm.validate(valid => {
|
async handleLogin() {
|
||||||
if (valid) {
|
const login_public_key = await preLogin();
|
||||||
this.loading = true;
|
this.$refs.loginForm.validate(valid => {
|
||||||
if (this.loginForm.rememberMe) {
|
if (valid) {
|
||||||
Cookies.set("username", this.loginForm.username, { expires: 30 });
|
this.loading = true;
|
||||||
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
|
if (this.loginForm.rememberMe) {
|
||||||
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
|
Cookies.set("username", this.loginForm.username, { expires: 30 });
|
||||||
} else {
|
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
|
||||||
Cookies.remove("username");
|
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
|
||||||
Cookies.remove("password");
|
} else {
|
||||||
Cookies.remove('rememberMe');
|
Cookies.remove("username");
|
||||||
}
|
Cookies.remove("password");
|
||||||
this.$store.dispatch("Login", this.loginForm).then(() => {
|
Cookies.remove('rememberMe');
|
||||||
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
|
}
|
||||||
}).catch(() => {
|
let authLoginForm = {};
|
||||||
this.loading = false;
|
// encrypt 加密
|
||||||
if (this.captchaOnOff) {
|
authLoginForm.username = encryptLogin(login_public_key, this.loginForm.username);
|
||||||
this.getCode();
|
authLoginForm.password = encryptLogin(login_public_key, this.loginForm.password);
|
||||||
}
|
authLoginForm.code = this.loginForm.code;
|
||||||
});
|
authLoginForm.uuid = this.loginForm.uuid;
|
||||||
}
|
authLoginForm.publicKey = login_public_key;
|
||||||
});
|
console.log(authLoginForm);
|
||||||
}
|
this.$store.dispatch("Login", authLoginForm).then(() => {
|
||||||
}
|
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
|
||||||
};
|
}).catch(() => {
|
||||||
</script>
|
this.loading = false;
|
||||||
|
if (this.captchaOnOff) {
|
||||||
<style rel="stylesheet/scss" lang="scss">
|
this.getCode();
|
||||||
.login {
|
}
|
||||||
display: flex;
|
});
|
||||||
justify-content: center;
|
}
|
||||||
align-items: center;
|
});
|
||||||
height: 100%;
|
}
|
||||||
background-image: url("../assets/images/login-background.jpg");
|
}
|
||||||
background-size: cover;
|
};
|
||||||
}
|
</script>
|
||||||
.title {
|
|
||||||
margin: 0px auto 30px auto;
|
<style rel="stylesheet/scss" lang="scss">
|
||||||
text-align: center;
|
.login {
|
||||||
color: #707070;
|
display: flex;
|
||||||
}
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
.login-form {
|
height: 100%;
|
||||||
border-radius: 6px;
|
background-image: url("../assets/images/login-background.jpg");
|
||||||
background: #ffffff;
|
background-size: cover;
|
||||||
width: 400px;
|
}
|
||||||
padding: 25px 25px 5px 25px;
|
.title {
|
||||||
.el-input {
|
margin: 0px auto 30px auto;
|
||||||
height: 38px;
|
text-align: center;
|
||||||
input {
|
color: #707070;
|
||||||
height: 38px;
|
}
|
||||||
}
|
|
||||||
}
|
.login-form {
|
||||||
.input-icon {
|
border-radius: 6px;
|
||||||
height: 39px;
|
background: #ffffff;
|
||||||
width: 14px;
|
width: 400px;
|
||||||
margin-left: 2px;
|
padding: 25px 25px 5px 25px;
|
||||||
}
|
.el-input {
|
||||||
}
|
height: 38px;
|
||||||
.login-tip {
|
input {
|
||||||
font-size: 13px;
|
height: 38px;
|
||||||
text-align: center;
|
}
|
||||||
color: #bfbfbf;
|
}
|
||||||
}
|
.input-icon {
|
||||||
.login-code {
|
height: 39px;
|
||||||
width: 33%;
|
width: 14px;
|
||||||
height: 38px;
|
margin-left: 2px;
|
||||||
float: right;
|
}
|
||||||
img {
|
}
|
||||||
cursor: pointer;
|
.login-tip {
|
||||||
vertical-align: middle;
|
font-size: 13px;
|
||||||
}
|
text-align: center;
|
||||||
}
|
color: #bfbfbf;
|
||||||
.el-login-footer {
|
}
|
||||||
height: 40px;
|
.login-code {
|
||||||
line-height: 40px;
|
width: 33%;
|
||||||
position: fixed;
|
height: 38px;
|
||||||
bottom: 0;
|
float: right;
|
||||||
width: 100%;
|
img {
|
||||||
text-align: center;
|
cursor: pointer;
|
||||||
color: #fff;
|
vertical-align: middle;
|
||||||
font-family: Arial;
|
}
|
||||||
font-size: 12px;
|
}
|
||||||
letter-spacing: 1px;
|
.el-login-footer {
|
||||||
}
|
height: 40px;
|
||||||
.login-code-img {
|
line-height: 40px;
|
||||||
height: 38px;
|
position: fixed;
|
||||||
}
|
bottom: 0;
|
||||||
</style>
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
color: #fff;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 12px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
.login-code-img {
|
||||||
|
height: 38px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user