Browse Source

👣 完成用户登录功能

master
wangxiang 3 years ago
parent
commit
580f7985f5
  1. 33
      kicc-auth/src/main/java/com/cloud/kicc/auth/endpoint/KiccTokenEndpoint.java
  2. 3
      kicc-ui/.env
  3. 35
      kicc-ui/src/api/controller/sys/user.ts
  4. 12
      kicc-ui/src/api/controller/system/menu.ts
  5. 12
      kicc-ui/src/api/controller/system/role.ts
  6. 16
      kicc-ui/src/api/controller/system/user.ts
  7. 2
      kicc-ui/src/hooks/setting/index.ts
  8. 6
      kicc-ui/src/store/modules/user.ts
  9. 2
      kicc-ui/src/utils/env.ts
  10. 4
      kicc-ui/types/config.d.ts

33
kicc-auth/src/main/java/com/cloud/kicc/auth/endpoint/kiccTokenEndpoint.java → kicc-auth/src/main/java/com/cloud/kicc/auth/endpoint/KiccTokenEndpoint.java

@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.event.LogoutSuccessEvent; import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken; import org.springframework.security.oauth2.common.OAuth2RefreshToken;
@ -44,7 +45,7 @@ import java.util.stream.Collectors;
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping("/token") @RequestMapping("/token")
public class kiccTokenEndpoint { public class KiccTokenEndpoint {
private final ClientDetailsService clientDetailsService; private final ClientDetailsService clientDetailsService;
@ -98,11 +99,19 @@ public class kiccTokenEndpoint {
@DeleteMapping("/logout") @DeleteMapping("/logout")
public R<Boolean> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) { public R<Boolean> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
if (StrUtil.isBlank(authHeader)) { if (StrUtil.isBlank(authHeader)) {
return R.ok(); return R.error("退出失败,token 为空");
} }
String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim(); String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim();
return removeToken(tokenValue); OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) {
return R.error("退出失败,token 无效");
}
// 清空access token
tokenStore.removeAccessToken(accessToken);
// 清空 refresh token
OAuth2RefreshToken refreshToken = accessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
return R.ok();
} }
/** /**
@ -111,21 +120,22 @@ public class kiccTokenEndpoint {
*/ */
@Inner @Inner
@DeleteMapping("/{token}") @DeleteMapping("/{token}")
@PreAuthorize("@ps.hasPerm('token_del')")
public R<Boolean> removeToken(@PathVariable("token") String token) { public R<Boolean> removeToken(@PathVariable("token") String token) {
OAuth2AccessToken accessToken = tokenStore.readAccessToken(token); OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) { if (oAuth2AccessToken == null || StrUtil.isBlank(oAuth2AccessToken.getValue())) {
return R.ok(); return R.ok(Boolean.TRUE, "操作失败,token 无效");
} }
OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(accessToken); OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(oAuth2AccessToken);
// 清空用户信息 // 清空用户信息
cacheManager.getCache(CacheConstants.USER_DETAILS).evict(auth2Authentication.getName()); cacheManager.getCache(CacheConstants.USER_DETAILS).evict(auth2Authentication.getName());
// 清空access token // 清空access token
tokenStore.removeAccessToken(accessToken); tokenStore.removeAccessToken(oAuth2AccessToken);
// 清空 refresh token // 清空 refresh token
OAuth2RefreshToken refreshToken = accessToken.getRefreshToken(); OAuth2RefreshToken refreshToken = oAuth2AccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken); tokenStore.removeRefreshToken(refreshToken);
// 处理自定义退出事件,保存相关日志 // 处理自定义退出事件,保存相关日志
@ -140,9 +150,10 @@ public class kiccTokenEndpoint {
*/ */
@Inner @Inner
@PostMapping("/list") @PostMapping("/list")
@PreAuthorize("@ps.hasPerm('token_view')")
public R tokenList(@RequestBody Map<String, Object> params) { public R tokenList(@RequestBody Map<String, Object> params) {
// 根据分页参数获取对应数据 // 根据分页参数获取对应数据
String key = String.format("%sauth_to_access:*", CacheConstants.OAUTH_ACCESS); String key = String.format("%south_to_access:*", CacheConstants.OAUTH_ACCESS);
int current = MapUtil.getInt(params, CommonConstants.CURRENT); int current = MapUtil.getInt(params, CommonConstants.CURRENT);
int size = MapUtil.getInt(params, CommonConstants.SIZE); int size = MapUtil.getInt(params, CommonConstants.SIZE);
Set<String> keys = redisTemplate.keys(key); Set<String> keys = redisTemplate.keys(key);

3
kicc-ui/.env

@ -12,3 +12,6 @@ VITE_GLOB_CLIENT_ID = kicc
# client-secret # client-secret
VITE_GLOB_CLIENT_SECRET = kicc VITE_GLOB_CLIENT_SECRET = kicc
# 网关ase密码解密密钥,保持跟后端密钥一致,必须要有否则登录会失败的
VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET = changsha-kanglai

35
kicc-ui/src/api/controller/sys/user.ts

@ -3,21 +3,22 @@ import {GetCaptchaModel, GetUserInfoModel, LoginParams, LoginResultModel,} from
import {encryptionLogin} from '/@/utils/cipher'; import {encryptionLogin} from '/@/utils/cipher';
import qs from 'qs'; import qs from 'qs';
import {RequestOptions} from "/#/axios"; import {RequestOptions} from "/#/axios";
import {useGlobSetting} from "/@/hooks/setting";
const globSetting = useGlobSetting();
enum Api { enum Api {
Login = '/auth_proxy/oauth/token', login = '/auth_proxy/oauth/token',
Logout = '/mate-uaa/auth/logout', logout = '/auth_proxy/token/logout',
GetUserInfo = '/system_proxy/system/user/info', getUserInfo = '/system_proxy/system/user/info',
GetPermCode = '/getPermCode', getCaptcha = '/code',
GetCaptcha = '/code',
} }
/** 用户登录接口 */ /** 用户登录接口 */
export function loginApi(params: LoginParams, options?: boolean | RequestOptions) { export function login(params: LoginParams, options?: boolean | RequestOptions) {
// 非对称密钥ASE加密处理 // 非对称密钥ASE加密处理
const user = encryptionLogin({ const user = encryptionLogin({
data: params, data: params,
key: 'changsha-kanglai', key: globSetting.gatewayAseEncodeSecret,
param: ['password'] param: ['password']
}); });
@ -30,7 +31,7 @@ export function loginApi(params: LoginParams, options?: boolean | RequestOptions
const data = qs.stringify({'username': username, 'password': password}); const data = qs.stringify({'username': username, 'password': password});
return defHttp.post<LoginResultModel>({ return defHttp.post<LoginResultModel>({
url: Api.Login, url: Api.login,
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
}, },
@ -39,21 +40,17 @@ export function loginApi(params: LoginParams, options?: boolean | RequestOptions
}, <RequestOptions>options); }, <RequestOptions>options);
} }
/** /** 获取当前用户信息 */
* @description: getUserInfo
*/
export function getUserInfo() { export function getUserInfo() {
return defHttp.get<GetUserInfoModel>({ url: Api.GetUserInfo }); return defHttp.get<GetUserInfoModel>({ url: Api.getUserInfo });
} }
export function getPermCode() { /** 登出 */
return defHttp.get<string[]>({ url: Api.GetPermCode }); export function logout() {
} return defHttp.delete({ url: Api.logout });
export function doLogout() {
return defHttp.post({ url: Api.Logout });
} }
/** 获取验证码 */
export function getCaptcha() { export function getCaptcha() {
return defHttp.get<GetCaptchaModel>({ url: `${Api.GetCaptcha}?key=${Date.now()}` }); return defHttp.get<GetCaptchaModel>({ url: `${Api.getCaptcha}?key=${Date.now()}` });
} }

12
kicc-ui/src/api/controller/system/menu.ts

@ -16,20 +16,20 @@ enum Api {
roleMenuIds = '/system_proxy/system/menu/roleMenuTree' roleMenuIds = '/system_proxy/system/menu/roleMenuTree'
} }
// 查询菜单列表 /** 查询菜单列表 */
export const listMenu = (params?: Partial<MenuDto>) => defHttp.get({ url: Api.list, params }); export const listMenu = (params?: Partial<MenuDto>) => defHttp.get({ url: Api.list, params });
// 新增菜单 /** 新增菜单 */
export const addMenu = (params: Partial<Menu>) => defHttp.post({ url: Api.add, data: params }); export const addMenu = (params: Partial<Menu>) => defHttp.post({ url: Api.add, data: params });
// 修改菜单 /** 修改菜单 */
export const editMenu = (params: Partial<Menu>) => defHttp.put({ url: Api.edit, data: params }); export const editMenu = (params: Partial<Menu>) => defHttp.put({ url: Api.edit, data: params });
// 查询菜单详细 /** 查询菜单详细 */
export const getMenu = (id: string) => defHttp.get<Menu>({ url: `${Api.get}/${id}` }); export const getMenu = (id: string) => defHttp.get<Menu>({ url: `${Api.get}/${id}` });
// 删除菜单 /** 删除菜单 */
export const delMenu = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` }); export const delMenu = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });
// 通过角色编号查询菜单编号 /** 通过角色编号查询菜单编号 */
export const getRoleMenuIds = (roleId : string) => defHttp.get<ResultVo<any>>({ url: `${Api.roleMenuIds}/${roleId}`}); export const getRoleMenuIds = (roleId : string) => defHttp.get<ResultVo<any>>({ url: `${Api.roleMenuIds}/${roleId}`});

12
kicc-ui/src/api/controller/system/role.ts

@ -15,20 +15,20 @@ enum Api {
changeStatus = '/system_proxy/system/role/changeStatus' changeStatus = '/system_proxy/system/role/changeStatus'
} }
// 查询角色列表 /** 查询角色列表 */
export const listRole = (params?: Partial<RoleDto>) => defHttp.get<RoleVo>({ url: Api.list, params }, { isReturnResultResponse: true }); export const listRole = (params?: Partial<RoleDto>) => defHttp.get<RoleVo>({ url: Api.list, params }, { isReturnResultResponse: true });
// 新增角色 /** 新增角色 */
export const addRole = (params: Partial<Role>) => defHttp.post({ url: Api.add, data: params }); export const addRole = (params: Partial<Role>) => defHttp.post({ url: Api.add, data: params });
// 修改角色 /** 修改角色 */
export const editRole = (params: Partial<Role>) => defHttp.put({ url: Api.edit, data: params }); export const editRole = (params: Partial<Role>) => defHttp.put({ url: Api.edit, data: params });
// 查询角色详细 /** 查询角色详细 */
export const getRole = (id: string) => defHttp.get<Role>({ url: `${Api.get}/${id}` }); export const getRole = (id: string) => defHttp.get<Role>({ url: `${Api.get}/${id}` });
// 删除角色 /** 删除角色 */
export const delRole = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` }); export const delRole = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });
// 修改角色状态 /** 修改角色状态 */
export const changeStatus = (id: string, status: string) => defHttp.put({ url: Api.changeStatus, data: { id: id, status: status } }); export const changeStatus = (id: string, status: string) => defHttp.put({ url: Api.changeStatus, data: { id: id, status: status } });

16
kicc-ui/src/api/controller/system/user.ts

@ -18,26 +18,26 @@ enum Api {
changeStatus='/system_proxy/system/user/changeStatus' changeStatus='/system_proxy/system/user/changeStatus'
} }
// 查询用户列表 /** 查询用户列表 */
export const listUser = (params?: Partial<UserDto>) => defHttp.get<UserVo>({ url: Api.list, params }, { isReturnResultResponse: true }); export const listUser = (params?: Partial<UserDto>) => defHttp.get<UserVo>({ url: Api.list, params }, { isReturnResultResponse: true });
// 新增用户 /** 新增用户 */
export const addUser = (params: Partial<User>) => defHttp.post({ url: Api.add, data: params }); export const addUser = (params: Partial<User>) => defHttp.post({ url: Api.add, data: params });
// 修改用户 /** 修改用户 */
export const editUser = (params: Partial<User>) => defHttp.put({ url: Api.edit, data: params }); export const editUser = (params: Partial<User>) => defHttp.put({ url: Api.edit, data: params });
// 查询用户详细 /** 查询用户详细 */
export const getUser = (id: string) => defHttp.get<ResultVo<any>>({ url: `${Api.get}/${id}` }); export const getUser = (id: string) => defHttp.get<ResultVo<any>>({ url: `${Api.get}/${id}` });
// 删除用户 /** 删除用户 */
export const delUser = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` }); export const delUser = (id: string) => defHttp.delete({ url: `${Api.del}/${id}` });
// 更新密码 /** 更新密码 */
export const updatePwd = (params: Partial<User>) => defHttp.put({ url: Api.updatePwd, params }); export const updatePwd = (params: Partial<User>) => defHttp.put({ url: Api.updatePwd, params });
// 重置密码 /** 重置密码 */
export const resetPwd = (params: Partial<User>) => defHttp.put({ url: Api.resetPwd, data: params }); export const resetPwd = (params: Partial<User>) => defHttp.put({ url: Api.resetPwd, data: params });
// 修改用户状态 /** 修改用户状态 */
export const changeStatus = (id: string, status: string) => defHttp.put({ url: Api.changeStatus, data: { id: id, status: status } }); export const changeStatus = (id: string, status: string) => defHttp.put({ url: Api.changeStatus, data: { id: id, status: status } });

2
kicc-ui/src/hooks/setting/index.ts

@ -18,6 +18,7 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
VITE_GLOB_UPLOAD_URL, VITE_GLOB_UPLOAD_URL,
VITE_GLOB_CLIENT_ID, VITE_GLOB_CLIENT_ID,
VITE_GLOB_CLIENT_SECRET, VITE_GLOB_CLIENT_SECRET,
VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET
} = getAppEnvConfig(); } = getAppEnvConfig();
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) { if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
@ -33,6 +34,7 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
uploadUrl: VITE_GLOB_UPLOAD_URL, uploadUrl: VITE_GLOB_UPLOAD_URL,
clientId: VITE_GLOB_CLIENT_ID, clientId: VITE_GLOB_CLIENT_ID,
clientSecret: VITE_GLOB_CLIENT_SECRET, clientSecret: VITE_GLOB_CLIENT_SECRET,
gatewayAseEncodeSecret: VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET
}; };
return glob as Readonly<GlobConfig>; return glob as Readonly<GlobConfig>;
}; };

6
kicc-ui/src/store/modules/user.ts

@ -12,7 +12,7 @@ import {PageEnum} from '/@/enums/pageEnum';
import {ACCESS_TOKEN_KEY, PERMISSIONS_KEY, REFRESH_TOKEN_KEY, ROLE_IDS_KEY, USER_INFO_KEY} from '/@/enums/cacheEnum'; import {ACCESS_TOKEN_KEY, PERMISSIONS_KEY, REFRESH_TOKEN_KEY, ROLE_IDS_KEY, USER_INFO_KEY} from '/@/enums/cacheEnum';
import {getAuthCache, setAuthCache} from '/@/utils/auth'; import {getAuthCache, setAuthCache} from '/@/utils/auth';
import {GetUserInfoModel, LoginParams} from '/@/api/controller/sys/model/userModel'; import {GetUserInfoModel, LoginParams} from '/@/api/controller/sys/model/userModel';
import {doLogout, getUserInfo, loginApi} from '/@/api/controller/sys/user'; import {logout, getUserInfo, login} from '/@/api/controller/sys/user';
import {useI18n} from '/@/hooks/web/useI18n'; import {useI18n} from '/@/hooks/web/useI18n';
import {useMessage} from '/@/hooks/web/useMessage'; import {useMessage} from '/@/hooks/web/useMessage';
import {router} from '/@/router'; import {router} from '/@/router';
@ -101,7 +101,7 @@ export const useUserStore = defineStore({
async login(params: LoginParams & { goHome?: boolean; unLock?: boolean; }): Promise<GetUserInfoModel | null> { async login(params: LoginParams & { goHome?: boolean; unLock?: boolean; }): Promise<GetUserInfoModel | null> {
try { try {
const { goHome = true, unLock = false, ...loginParams } = params; const { goHome = true, unLock = false, ...loginParams } = params;
const data = await loginApi(loginParams, unLock && { const data = await login(loginParams, unLock && {
// 使用微服务提供的锁屏专属客户端不需要写验证码进行登录 // 使用微服务提供的锁屏专属客户端不需要写验证码进行登录
clientId: 'kicc_lock', clientId: 'kicc_lock',
clientSecret: 'kicc_lock' clientSecret: 'kicc_lock'
@ -144,7 +144,7 @@ export const useUserStore = defineStore({
/** 登出 */ /** 登出 */
async logout(goLogin = false) { async logout(goLogin = false) {
try { try {
await doLogout(); await logout();
} catch { } catch {
console.log('注销Token失败'); console.log('注销Token失败');
} }

2
kicc-ui/src/utils/env.ts

@ -36,6 +36,7 @@ export function getAppEnvConfig() {
VITE_GLOB_UPLOAD_URL, VITE_GLOB_UPLOAD_URL,
VITE_GLOB_CLIENT_ID, VITE_GLOB_CLIENT_ID,
VITE_GLOB_CLIENT_SECRET, VITE_GLOB_CLIENT_SECRET,
VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET
} = ENV; } = ENV;
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) { if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
@ -52,6 +53,7 @@ export function getAppEnvConfig() {
VITE_GLOB_UPLOAD_URL, VITE_GLOB_UPLOAD_URL,
VITE_GLOB_CLIENT_ID, VITE_GLOB_CLIENT_ID,
VITE_GLOB_CLIENT_SECRET, VITE_GLOB_CLIENT_SECRET,
VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET
}; };
} }

4
kicc-ui/types/config.d.ts vendored

@ -151,6 +151,8 @@ export interface GlobConfig {
clientId: string; clientId: string;
// clientSecret // clientSecret
clientSecret: string; clientSecret: string;
// 网关ase密码解密密钥,保持跟后端密钥一致,必须要有否则登录会失败的
gatewayAseEncodeSecret: string;
} }
export interface GlobEnvConfig { export interface GlobEnvConfig {
@ -168,4 +170,6 @@ export interface GlobEnvConfig {
VITE_GLOB_CLIENT_ID: string; VITE_GLOB_CLIENT_ID: string;
// clientSecret // clientSecret
VITE_GLOB_CLIENT_SECRET: string; VITE_GLOB_CLIENT_SECRET: string;
// 网关ase密码解密密钥,保持跟后端密钥一致,必须要有否则登录会失败的
VITE_GLOB_GATEWAY_ASE_ENCODE_SECRET: string;
} }

Loading…
Cancel
Save