feat: 钉钉应用内免登录功能
This commit is contained in:
parent
d695d81ed4
commit
5526c7ad4e
@ -28,6 +28,7 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
"/api/system-config/key/site.name", // 排除公开的网站设置API(按key获取)
|
||||
"/api/system-config/key/site.logo", // 排除公开的网站设置API(按key获取)
|
||||
"/api/system-config/map/group/site", // 排除公开的网站设置API(Map格式)
|
||||
"/api/dingtalk/config/public", // 排除钉钉公开配置API(免登需要)
|
||||
"/doc.html", // 排除Swagger文档
|
||||
"/swagger-ui/**",
|
||||
"/v3/api-docs/**",
|
||||
|
||||
@ -32,6 +32,20 @@ public class DingTalkController {
|
||||
private final ObjectMapper objectMapper;
|
||||
private final UserMapper userMapper;
|
||||
|
||||
@Operation(summary = "获取钉钉公开配置(用于免登)")
|
||||
@GetMapping("/config/public")
|
||||
public Result<Map<String, String>> getPublicConfig() {
|
||||
DingTalkConfig config = dingTalkService.getConfig();
|
||||
|
||||
Map<String, String> publicConfig = new HashMap<>();
|
||||
if (config != null && config.getEnabled() == 1) {
|
||||
publicConfig.put("corpId", config.getCorpId());
|
||||
publicConfig.put("agentId", config.getAgentId());
|
||||
}
|
||||
|
||||
return Result.success(publicConfig);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取钉钉配置")
|
||||
@GetMapping("/config")
|
||||
public Result<DingTalkConfig> getConfig() {
|
||||
|
||||
@ -152,6 +152,52 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 钉钉免登录(使用授权码)
|
||||
*/
|
||||
const loginWithDingTalk = async (authCode: string) => {
|
||||
try {
|
||||
const response = await authApi.login({
|
||||
authCode,
|
||||
loginType: 'dingtalk'
|
||||
})
|
||||
|
||||
const loginData = response.data
|
||||
|
||||
// 保存 token
|
||||
const accessToken = loginData.token || loginData.accessToken
|
||||
token.value = accessToken
|
||||
localStorage.setItem('token', accessToken)
|
||||
|
||||
// 登录成功后获取完整用户信息
|
||||
try {
|
||||
await fetchUserInfo()
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
// 使用基本信息
|
||||
const userInfoData = {
|
||||
id: loginData.userId,
|
||||
username: loginData.username,
|
||||
realName: loginData.realName,
|
||||
avatar: loginData.avatar,
|
||||
email: '',
|
||||
phone: '',
|
||||
deptId: null,
|
||||
deptName: '',
|
||||
roles: loginData.roles || [],
|
||||
permissions: []
|
||||
}
|
||||
userInfo.value = userInfoData
|
||||
localStorage.setItem('userInfo', JSON.stringify(userInfoData))
|
||||
}
|
||||
|
||||
return { success: true }
|
||||
} catch (error: any) {
|
||||
console.error('钉钉免登录失败:', error)
|
||||
return { success: false, message: error.message || '钉钉免登录失败' }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
userInfo,
|
||||
@ -159,6 +205,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
userName,
|
||||
userRoles,
|
||||
login,
|
||||
loginWithDingTalk,
|
||||
logout,
|
||||
fetchUserInfo,
|
||||
refreshToken
|
||||
|
||||
@ -258,6 +258,13 @@ const registerForm = reactive({
|
||||
// 钉钉登录
|
||||
const dingTalkLoginEnabled = ref(false)
|
||||
const dingTalkLoginLoading = ref(false)
|
||||
const dingTalkAutoLoginInProgress = ref(false)
|
||||
|
||||
// 检测是否在钉钉环境内
|
||||
const isDingTalkEnv = () => {
|
||||
const ua = navigator.userAgent.toLowerCase()
|
||||
return ua.includes('dingtalk')
|
||||
}
|
||||
|
||||
// 部门列表
|
||||
const departments = ref<any[]>([])
|
||||
@ -411,7 +418,7 @@ const checkRegisterEnabled = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 处理钉钉登录
|
||||
// 处理钉钉登录(OAuth方式,用于非钉钉环境)
|
||||
const handleDingTalkLogin = async () => {
|
||||
dingTalkLoginLoading.value = true
|
||||
try {
|
||||
@ -438,7 +445,98 @@ const handleDingTalkLogin = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 钉钉应用内免登录
|
||||
const handleDingTalkAutoLogin = async () => {
|
||||
if (dingTalkAutoLoginInProgress.value) return
|
||||
dingTalkAutoLoginInProgress.value = true
|
||||
|
||||
console.log('检测到钉钉环境,尝试免登录...')
|
||||
|
||||
try {
|
||||
// 1. 先获取钉钉配置(corpId)
|
||||
const configResponse = await request.get<Result<{ corpId: string; agentId: string }>>('/dingtalk/config/public')
|
||||
if (configResponse.code !== 200 || !configResponse.data?.corpId) {
|
||||
console.log('钉钉配置未完成,跳过免登录')
|
||||
return
|
||||
}
|
||||
|
||||
const corpId = configResponse.data.corpId
|
||||
console.log('获取到corpId:', corpId)
|
||||
|
||||
// 2. 动态加载钉钉JSAPI
|
||||
if (!(window as any).dd) {
|
||||
await loadDingTalkJSAPI()
|
||||
}
|
||||
|
||||
const dd = (window as any).dd
|
||||
if (!dd) {
|
||||
console.error('钉钉JSAPI加载失败')
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 使用钉钉JSAPI获取免登授权码
|
||||
dd.ready(() => {
|
||||
dd.runtime.permission.requestAuthCode({
|
||||
corpId: corpId,
|
||||
onSuccess: async (result: { code: string }) => {
|
||||
console.log('获取到免登授权码:', result.code)
|
||||
try {
|
||||
// 4. 使用授权码登录
|
||||
const loginResult = await authStore.loginWithDingTalk(result.code)
|
||||
if (loginResult.success) {
|
||||
message.success('钉钉免登录成功')
|
||||
router.push('/')
|
||||
} else {
|
||||
console.log('钉钉免登录失败:', loginResult.message)
|
||||
// 免登失败不显示错误,让用户手动登录
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('钉钉免登录请求失败:', error)
|
||||
} finally {
|
||||
dingTalkAutoLoginInProgress.value = false
|
||||
}
|
||||
},
|
||||
onFail: (err: any) => {
|
||||
console.log('获取免登授权码失败:', err)
|
||||
dingTalkAutoLoginInProgress.value = false
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
dd.error((err: any) => {
|
||||
console.error('钉钉JSAPI错误:', err)
|
||||
dingTalkAutoLoginInProgress.value = false
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('钉钉免登录异常:', error)
|
||||
dingTalkAutoLoginInProgress.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 动态加载钉钉JSAPI
|
||||
const loadDingTalkJSAPI = (): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if ((window as any).dd) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.src = 'https://g.alicdn.com/dingding/dingtalk-jsapi/3.0.12/dingtalk.open.js'
|
||||
script.onload = () => {
|
||||
console.log('钉钉JSAPI加载成功')
|
||||
resolve()
|
||||
}
|
||||
script.onerror = () => {
|
||||
console.error('钉钉JSAPI加载失败')
|
||||
reject(new Error('Failed to load DingTalk JSAPI'))
|
||||
}
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// 加载网站设置
|
||||
siteSettingsStore.loadSiteSettings()
|
||||
|
||||
@ -446,11 +544,17 @@ onMounted(() => {
|
||||
loadDepartments()
|
||||
|
||||
// 检查钉钉登录是否启用
|
||||
checkDingTalkLogin()
|
||||
await checkDingTalkLogin()
|
||||
|
||||
// 检查注册功能是否启用
|
||||
checkRegisterEnabled()
|
||||
|
||||
// 如果在钉钉环境内且启用了钉钉登录,尝试自动免登
|
||||
if (isDingTalkEnv() && dingTalkLoginEnabled.value) {
|
||||
console.log('检测到钉钉环境,启动免登录流程')
|
||||
handleDingTalkAutoLogin()
|
||||
}
|
||||
|
||||
// 监听网站设置更新事件(在加载后监听,确保能接收到事件)
|
||||
window.addEventListener('siteSettingsUpdated', handleSiteSettingsUpdate)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user