diff --git a/threadpool/console/package.json b/threadpool/console/package.json index aa4202ab..505a900b 100755 --- a/threadpool/console/package.json +++ b/threadpool/console/package.json @@ -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", diff --git a/threadpool/console/src/store/modules/user.js b/threadpool/console/src/store/modules/user.js index bae7a0c1..59bd3d28 100755 --- a/threadpool/console/src/store/modules/user.js +++ b/threadpool/console/src/store/modules/user.js @@ -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, }; diff --git a/threadpool/console/src/views/login/index.vue b/threadpool/console/src/views/login/index.vue index 086359a3..66ce2387 100755 --- a/threadpool/console/src/views/login/index.vue +++ b/threadpool/console/src/views/login/index.vue @@ -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 { diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java index e64ab45c..408aa3d4 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java @@ -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; diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/biz/user/LoginUser.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/biz/user/LoginUser.java index 0c15778c..b5cd71d3 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/biz/user/LoginUser.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/biz/user/LoginUser.java @@ -24,6 +24,10 @@ import lombok.Data; */ @Data public class LoginUser { + /** + * encode key reverse + */ + private String tag; /** * username diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/toolkit/AESUtil.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/toolkit/AESUtil.java new file mode 100644 index 00000000..5a1ed033 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/toolkit/AESUtil.java @@ -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 Serenity + * @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); + } +}