Encrypted transmission of login password (#1316)

* feat(login): #1300 Encrypted transmission of login password

* feat(login): #1300 Modify the comments to English

* ref(login): #1300 Restore static resources

---------

Co-authored-by: limingwei <limingwei@century-cn.com>
Co-authored-by: Serenity <SerenitySir@outlook.com>
pull/1320/head
Serenity 1 year ago committed by GitHub
parent dd2c27ba3c
commit a9ca0a7b84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -44,9 +44,11 @@
},
"dependencies": {
"axios": ">=0.18.1",
"buffer": "^6.0.3",
"clipboard": "2.0.4",
"codemirror": "5.45.0",
"core-js": "^3.26.0",
"crypto": "^1.0.1",
"echarts": "^5.2.2",
"element-ui": "^2.15.7",
"fuse.js": "3.4.4",

@ -1,126 +1,155 @@
import { login } from '@/api/user';
import { getToken, setToken, removeToken } from '@/utils/auth';
import router, { resetRouter } from '@/router';
import {login} from '@/api/user';
import {getToken, removeToken, setToken} from '@/utils/auth';
import router, {resetRouter} from '@/router';
import {Buffer} from 'buffer'
import crypto from 'crypto'
const state = {
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: [],
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: [],
};
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction;
},
SET_NAME: (state, name) => {
state.name = name;
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar;
},
SET_ROLES: (state, roles) => {
state.roles = roles;
},
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction;
},
SET_NAME: (state, name) => {
state.name = name;
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar;
},
SET_ROLES: (state, roles) => {
state.roles = roles;
},
};
const actions = {
// user login
login({ commit }, userInfo) {
const { username, password } = userInfo;
return new Promise((resolve, reject) => {
login({ username: username.trim(), password: password, rememberMe: 1 })
.then((response) => {
const { data } = response;
const { roles } = response;
commit('SET_TOKEN', data);
localStorage.setItem('roles', JSON.stringify(roles));
localStorage.setItem('USER_ROLE', roles[0]);
setToken(data);
resolve();
})
.catch((error) => {
alert('登录失败');
reject(error);
// user login
login({commit}, userInfo) {
const {username, password} = userInfo;
return new Promise((resolve, reject) => {
let key = actions.genKey();
let encodePassword = actions.encrypt(password, key)
key = key.split("").reverse().join("")
login({username: username.trim(), password: encodePassword, tag: key, rememberMe: 1})
.then((response) => {
const {data} = response;
const {roles} = response;
commit('SET_TOKEN', data);
localStorage.setItem('roles', JSON.stringify(roles));
localStorage.setItem('USER_ROLE', roles[0]);
setToken(data);
resolve();
})
.catch((error) => {
// alert('登录失败');
reject(error);
});
});
});
},
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
const data = {};
data.roles = JSON.parse(localStorage.getItem('roles'));
commit('SET_ROLES', data.roles);
resolve(data);
});
},
// user logout
logout({ commit, state }) {
// return new Promise((resolve, reject) => {
// logout(state.token).then(() => {
// commit('SET_TOKEN', '')
// commit('SET_ROLES', [])
// removeToken()
// resetRouter()
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
return new Promise((resolve) => {
commit('SET_TOKEN', '');
commit('SET_ROLES', []);
removeToken();
resetRouter();
resolve();
});
},
// remove token
resetToken({ commit }) {
return new Promise((resolve) => {
commit('SET_TOKEN', '');
commit('SET_ROLES', []);
removeToken();
resolve();
});
},
// dynamically modify permissions
changeRoles({ commit, dispatch }, role) {
return new Promise(async (resolve) => {
const token = role + '-token';
commit('SET_TOKEN', token);
setToken(token);
const { roles } = await dispatch('getInfo');
resetRouter();
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true });
// dynamically add accessible routes
router.addRoutes(accessRoutes);
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true });
resolve();
});
},
},
// get user info
getInfo({commit, state}) {
return new Promise((resolve, reject) => {
const data = {};
data.roles = JSON.parse(localStorage.getItem('roles'));
commit('SET_ROLES', data.roles);
resolve(data);
});
},
// user logout
logout({commit, state}) {
// return new Promise((resolve, reject) => {
// logout(state.token).then(() => {
// commit('SET_TOKEN', '')
// commit('SET_ROLES', [])
// removeToken()
// resetRouter()
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
return new Promise((resolve) => {
commit('SET_TOKEN', '');
commit('SET_ROLES', []);
removeToken();
resetRouter();
resolve();
});
},
// remove token
resetToken({commit}) {
return new Promise((resolve) => {
commit('SET_TOKEN', '');
commit('SET_ROLES', []);
removeToken();
resolve();
});
},
// dynamically modify permissions
changeRoles({commit, dispatch}, role) {
return new Promise(async (resolve) => {
const token = role + '-token';
commit('SET_TOKEN', token);
setToken(token);
const {roles} = await dispatch('getInfo');
resetRouter();
// generate accessible routes map based on roles
const accessRoutes = await dispatch('permission/generateRoutes', roles, {root: true});
// dynamically add accessible routes
router.addRoutes(accessRoutes);
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, {root: true});
resolve();
});
},
genKey() {
let chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let result = '';
for (let i = 16; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
return result;
},
encrypt(msg, key) {
try {
let pwd = Buffer.from(key)
let iv = crypto.randomBytes(12)
let cipher = crypto.createCipheriv('aes-128-gcm', pwd, iv)
let enc = cipher.update(msg, 'utf8', 'base64')
enc += cipher.final('base64')
let tags = cipher.getAuthTag()
enc = Buffer.from(enc, 'base64')
let totalLength = iv.length + enc.length + tags.length
let bufferMsg = Buffer.concat([iv, enc, tags], totalLength)
return bufferMsg.toString('base64')
} catch (e) {
console.log("Encrypt is error", e)
return null
}
},
};
export default {
namespaced: true,
state,
mutations,
actions,
namespaced: true,
state,
mutations,
actions,
};

@ -170,8 +170,8 @@ export default {
this.$router.push({ path: this.redirect || '/', query: this.otherQuery });
this.loading = false;
})
.catch(() => {
console.log('error catch.');
.catch((e) => {
console.log('error catch.',e);
this.loading = false;
});
} else {

@ -19,12 +19,14 @@ package cn.hippo4j.auth.filter;
import cn.hippo4j.auth.model.biz.user.JwtUser;
import cn.hippo4j.auth.model.biz.user.LoginUser;
import cn.hippo4j.auth.toolkit.AESUtil;
import cn.hippo4j.auth.toolkit.JwtTokenUtil;
import cn.hippo4j.auth.toolkit.ReturnT;
import cn.hippo4j.common.toolkit.JSONUtil;
import cn.hippo4j.server.common.base.Results;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.codec.DecodingException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -38,6 +40,7 @@ import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -69,10 +72,17 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte
Authentication authenticate = null;
try {
LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class);
String key = new StringBuffer(loginUser.getTag()).reverse().toString();
String password = AESUtil.decrypt(loginUser.getPassword(), key);
loginUser.setPassword(password);
request.setAttribute("loginUser", loginUser);
rememberMe.set(loginUser.getRememberMe());
authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList()));
} catch (GeneralSecurityException e) {
log.warn("Password decode exception: {}", e.getMessage());
throw new DecodingException(e.getMessage());
} catch (UsernameNotFoundException e) {
log.warn("User {} not found", e.getMessage());
throw e;

@ -24,6 +24,10 @@ import lombok.Data;
*/
@Data
public class LoginUser {
/**
* encode key reverse
*/
private String tag;
/**
* username

@ -0,0 +1,92 @@
package cn.hippo4j.auth.toolkit;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.experimental.UtilityClass;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;
/**
* AES encryption and decryption algorithm tool class
*
* @author <a href="mailto:SerenitySir@outlook.com">Serenity</a>
* @date 2023/5/21 14:37
* @since JDK1.8+
*/
@UtilityClass
public class AESUtil {
private static final String AES_GCM_CIPHER = "AES/GCM/PKCS5Padding";
/**
* encrypt
*
* @param data Content that needs to be encrypted
* @param key Encrypt the password
* @return byte[]
*/
public static byte[] encrypt(byte[] data, byte[] key) throws GeneralSecurityException {
SecretKeySpec sKeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance(AES_GCM_CIPHER);
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec);
byte[] iv = cipher.getIV();
byte[] encryptData = cipher.doFinal(data);
byte[] message = new byte[12 + data.length + 16];
System.arraycopy(iv, 0, message, 0, 12);
System.arraycopy(encryptData, 0, message, 12, encryptData.length);
return message;
}
/**
* decrypt
*
* @param data The content to be decrypted
* @param key Decryption key
* @return byte[]
*/
public static byte[] decrypt(byte[] data, byte[] key) throws GeneralSecurityException{
GCMParameterSpec iv = new GCMParameterSpec(128, data, 0, 12);
Cipher cipher = Cipher.getInstance(AES_GCM_CIPHER);
SecretKey key2 = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, key2, iv);
return cipher.doFinal(data, 12, data.length - 12);
}
/**
* encrypt
*
* @param data Content that needs to be encrypted
* @param key Encrypt the password
* @return String
*/
public static String encrypt(String data, String key) throws GeneralSecurityException {
byte[] valueByte = encrypt(data.getBytes(StandardCharsets.UTF_8), key.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(valueByte);
}
/**
* decrypt
*
* @param data The content to be decrypted is a base64 string
* @param key Decryption key
* @return String
*/
public static String decrypt(String data, String key) throws GeneralSecurityException {
byte[] originalData = Base64.getDecoder().decode(data.getBytes());
byte[] valueByte = decrypt(originalData, key.getBytes(StandardCharsets.UTF_8));
return new String(valueByte);
}
/**
* Generate a random string key
*
* @return The string key
*/
public static String generateRandomKey() {
return IdWorker.get32UUID().substring(0, 16);
}
}
Loading…
Cancel
Save