diff --git a/README.md b/README.md index 5277cbc5..96eeaf97 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ ContiNew Admin (Continue New Admin)中后台管理框架/脚手架,持续 > **Note** > 更多功能和优化正在赶来💦,最新项目计划和进展请关注 [GitHub Project](https://github.com/Charles7c/continew-admin/projects) 和 [CHANGELOG.md](https://github.com/Charles7c/continew-admin/blob/dev/CHANGELOG.md)。 -- 用户管理:提供用户的相关配置,新增用户后,默认密码为 123456 +- 用户管理:提供用户的相关配置,新增用户后,默认密码为 123456,且支持第三方账号登录 - 部门管理:可配置系统组织架构,树形表格展示 - 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限 - 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单 @@ -235,6 +235,7 @@ pnpm dev | Liquibase | 4.9.1 | 用于管理数据库版本,跟踪、管理和应用数据库变化。 | | Redis | 6.2.7 | 高性能的 key-value 数据库。 | | Redisson | 3.20.1 | 不仅仅是一个 Redis Java 客户端,同其他 Redis Java 客户端有着很大的区别,相比之下其他客户端提供的功能还仅仅停留在作为数据库驱动层面上,比如仅针对 Redis 提供连接方式,发送命令和处理返回结果等。而 Redisson 充分的利用了 Redis 键值数据库提供的一系列优势,基于 Java 实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。 | +| Just Auth | 1.16.5 | 开箱即用的整合第三方登录的开源组件,脱离繁琐的第三方登录 SDK,让登录变得 So easy! | | Easy Excel | 3.3.2 | 一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。 | | Easy Captcha | 1.6.2 | Java 图形验证码,支持 gif、中文、算术等类型,可用于 Java Web、JavaSE 等项目。 | | Knife4j | 4.3.0 | 前身是 swagger-bootstrap-ui,集 Swagger2 和 OpenAPI3 为一体的增强解决方案。本项目使用的是 knife4j-openapi3-spring-boot-starter 基于 OpenAPI3 规范,在 Spring Boot < 3.0.0-M1 的单体架构下可以直接引用此 starter,该模块包含了 UI 部分,底层基于 springdoc-openapi 项目。 | diff --git a/continew-admin-common/pom.xml b/continew-admin-common/pom.xml index 620ed57a..7ddbde24 100644 --- a/continew-admin-common/pom.xml +++ b/continew-admin-common/pom.xml @@ -115,6 +115,16 @@ limitations under the License. + + + com.xkcoding.justauth + justauth-spring-boot-starter + + + me.zhyd.oauth + JustAuth + + com.alibaba diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/justauth/JustAuthRedisStateCache.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/justauth/JustAuthRedisStateCache.java new file mode 100644 index 00000000..eb141cae --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/justauth/JustAuthRedisStateCache.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.common.config.justauth; + +import java.time.Duration; + +import top.charles7c.cnadmin.common.constant.CacheConsts; +import top.charles7c.cnadmin.common.util.RedisUtils; + +import me.zhyd.oauth.cache.AuthStateCache; + +/** + * Just Auth 自定义 State 缓存实现(Redis) + * + * @author Charles7c + * @since 2023/10/8 22:17 + */ +public class JustAuthRedisStateCache implements AuthStateCache { + + /** + * 存入缓存 + * + * @param key + * 缓存 key + * @param value + * 缓存内容 + */ + @Override + public void cache(String key, String value) { + // 参考:在 JustAuth 中,内置了一个基于 map 的 state 缓存器,默认缓存有效期为 3 分钟 + RedisUtils.setCacheObject(RedisUtils.formatKey(CacheConsts.SOCIAL_AUTH_STATE_KEY_PREFIX, key), value, + Duration.ofMinutes(3)); + } + + /** + * 存入缓存 + * + * @param key + * 缓存 key + * @param value + * 缓存内容 + * @param timeout + * 指定缓存过期时间(毫秒) + */ + @Override + public void cache(String key, String value, long timeout) { + RedisUtils.setCacheObject(RedisUtils.formatKey(CacheConsts.SOCIAL_AUTH_STATE_KEY_PREFIX, key), value, + Duration.ofMillis(timeout)); + } + + /** + * 获取缓存内容 + * + * @param key + * 缓存 key + * @return 缓存内容 + */ + @Override + public String get(String key) { + return RedisUtils.getCacheObject(RedisUtils.formatKey(CacheConsts.SOCIAL_AUTH_STATE_KEY_PREFIX, key)); + } + + /** + * 是否存在 key,如果对应 key 的 value 值已过期,也返回 false + * + * @param key + * 缓存 key + * @return true:存在 key,并且 value 没过期;false:key 不存在或者已过期 + */ + @Override + public boolean containsKey(String key) { + return RedisUtils.hasKey(RedisUtils.formatKey(CacheConsts.SOCIAL_AUTH_STATE_KEY_PREFIX, key)); + } +} \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/justauth/JustAuthStateConfiguration.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/justauth/JustAuthStateConfiguration.java new file mode 100644 index 00000000..7caea1af --- /dev/null +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/config/justauth/JustAuthStateConfiguration.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.common.config.justauth; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import me.zhyd.oauth.cache.AuthStateCache; + +/** + * Just Auth State 缓存配置 + * + * @author Charles7c + * @since 2023/10/8 22:17 + */ +@Configuration +public class JustAuthStateConfiguration { + + /** + * Just Auth State 缓存 Redis 适配 + */ + @Bean + public AuthStateCache authStateCache() { + return new JustAuthRedisStateCache(); + } +} \ No newline at end of file diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/CacheConsts.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/CacheConsts.java index a748b5fe..f49708f0 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/CacheConsts.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/CacheConsts.java @@ -67,4 +67,9 @@ public class CacheConsts { * 仪表盘缓存键前缀 */ public static final String DASHBOARD_KEY_PREFIX = "DASHBOARD"; + + /** + * 社交身份认证状态键前缀 + */ + public static final String SOCIAL_AUTH_STATE_KEY_PREFIX = "SOCIAL_AUTH_STATE"; } diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/SysConsts.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/SysConsts.java index 1d348c04..704ec355 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/SysConsts.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/constant/SysConsts.java @@ -33,6 +33,11 @@ public class SysConsts { */ public static final String ADMIN_ROLE_CODE = "admin"; + /** + * 顶级部门 ID + */ + public static final Long SUPER_DEPT_ID = 1L; + /** * 顶级父 ID */ @@ -53,6 +58,11 @@ public class SysConsts { */ public static final String LOGIN_URI = "/auth/login"; + /** + * 退出 URI + */ + public static final String LOGOUT_URI = "/auth/logout"; + /** * VO 描述类字段后缀 */ diff --git a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java index 1556ad5e..e8f51889 100644 --- a/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java +++ b/continew-admin-common/src/main/java/top/charles7c/cnadmin/common/util/helper/LoginHelper.java @@ -53,8 +53,9 @@ public class LoginHelper { * * @param loginUser * 登录用户信息 + * @return 令牌 */ - public static void login(LoginUser loginUser) { + public static String login(LoginUser loginUser) { // 记录登录信息 HttpServletRequest request = ServletUtils.getRequest(); loginUser.setClientIp(ServletUtil.getClientIP(request)); @@ -65,8 +66,10 @@ public class LoginHelper { // 登录并缓存用户信息 StpUtil.login(loginUser.getId()); SaHolder.getStorage().set(CacheConsts.LOGIN_USER_KEY, loginUser); - loginUser.setToken(StpUtil.getTokenValue()); + String tokenValue = StpUtil.getTokenValue(); + loginUser.setToken(tokenValue); StpUtil.getTokenSession().set(CacheConsts.LOGIN_USER_KEY, loginUser); + return tokenValue; } /** diff --git a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java index 01ba2dc7..66125048 100644 --- a/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java +++ b/continew-admin-monitor/src/main/java/top/charles7c/cnadmin/monitor/interceptor/LogInterceptor.java @@ -215,11 +215,16 @@ public class LogInterceptor implements HandlerInterceptor { private void logRequest(LogDO logDO, HttpServletRequest request) { logDO.setRequestUrl(StrUtil.isBlank(request.getQueryString()) ? request.getRequestURL().toString() : request.getRequestURL().append(StringConsts.QUESTION_MARK).append(request.getQueryString()).toString()); - logDO.setRequestMethod(request.getMethod()); + String method = request.getMethod(); + logDO.setRequestMethod(method); logDO.setRequestHeaders(this.desensitize(ServletUtil.getHeaderMap(request))); String requestBody = this.getRequestBody(request); logDO.setCreateUser(ObjectUtil.defaultIfNull(logDO.getCreateUser(), LoginHelper.getUserId())); - if (null == logDO.getCreateUser() && SysConsts.LOGIN_URI.equals(request.getRequestURI())) { + String requestURI = request.getRequestURI(); + if (requestURI.startsWith("/auth") && !SysConsts.LOGOUT_URI.equals(requestURI)) { + logDO.setCreateUser(null); + } + if (null == logDO.getCreateUser() && SysConsts.LOGIN_URI.equals(requestURI)) { LoginRequest loginRequest = JSONUtil.toBean(requestBody, LoginRequest.class); logDO.setCreateUser( ExceptionUtils.exToNull(() -> userService.getByUsername(loginRequest.getUsername()).getId())); diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java index 4ef55e5e..b24b9943 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/LoginService.java @@ -20,6 +20,8 @@ import java.util.List; import top.charles7c.cnadmin.auth.model.vo.RouteVO; +import me.zhyd.oauth.model.AuthUser; + /** * 登录业务接口 * @@ -39,6 +41,15 @@ public interface LoginService { */ String login(String username, String password); + /** + * 社交身份登录 + * + * @param authUser + * 社交身份信息 + * @return 令牌 + */ + String socialLogin(AuthUser authUser); + /** * 构建路由树 * diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java index 4b46c7ee..5a64adee 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/auth/service/impl/LoginServiceImpl.java @@ -16,6 +16,7 @@ package top.charles7c.cnadmin.auth.service.impl; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -23,32 +24,38 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.json.JSONUtil; import top.charles7c.cnadmin.auth.model.vo.MetaVO; import top.charles7c.cnadmin.auth.model.vo.RouteVO; import top.charles7c.cnadmin.auth.service.LoginService; import top.charles7c.cnadmin.auth.service.PermissionService; import top.charles7c.cnadmin.common.annotation.TreeField; +import top.charles7c.cnadmin.common.constant.RegexConsts; import top.charles7c.cnadmin.common.constant.SysConsts; import top.charles7c.cnadmin.common.enums.DisEnableStatusEnum; +import top.charles7c.cnadmin.common.enums.GenderEnum; import top.charles7c.cnadmin.common.enums.MenuTypeEnum; import top.charles7c.cnadmin.common.model.dto.LoginUser; import top.charles7c.cnadmin.common.util.SecureUtils; import top.charles7c.cnadmin.common.util.TreeUtils; import top.charles7c.cnadmin.common.util.helper.LoginHelper; import top.charles7c.cnadmin.common.util.validate.CheckUtils; +import top.charles7c.cnadmin.system.model.entity.RoleDO; import top.charles7c.cnadmin.system.model.entity.UserDO; +import top.charles7c.cnadmin.system.model.entity.UserSocialDO; import top.charles7c.cnadmin.system.model.vo.DeptDetailVO; import top.charles7c.cnadmin.system.model.vo.MenuVO; -import top.charles7c.cnadmin.system.service.DeptService; -import top.charles7c.cnadmin.system.service.MenuService; -import top.charles7c.cnadmin.system.service.RoleService; -import top.charles7c.cnadmin.system.service.UserService; +import top.charles7c.cnadmin.system.service.*; + +import me.zhyd.oauth.model.AuthUser; /** * 登录业务实现 @@ -65,6 +72,8 @@ public class LoginServiceImpl implements LoginService { private final RoleService roleService; private final MenuService menuService; private final PermissionService permissionService; + private final UserRoleService userRoleService; + private final UserSocialService userSocialService; @Override public String login(String username, String password) { @@ -72,16 +81,44 @@ public class LoginServiceImpl implements LoginService { CheckUtils.throwIfNull(user, "用户名或密码错误"); Long userId = user.getId(); CheckUtils.throwIfNotEqual(SecureUtils.md5Salt(password, userId.toString()), user.getPassword(), "用户名或密码错误"); - CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, user.getStatus(), "此账号已被禁用,如有疑问,请联系管理员"); - DeptDetailVO deptDetailVO = deptService.get(user.getDeptId()); - CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, deptDetailVO.getStatus(), "此账号部门已被禁用,如有疑问,请联系管理员"); - // 登录并缓存用户信息 - LoginUser loginUser = BeanUtil.copyProperties(user, LoginUser.class); - loginUser.setPermissions(permissionService.listPermissionByUserId(userId)); - loginUser.setRoleCodes(permissionService.listRoleCodeByUserId(userId)); - loginUser.setRoles(roleService.listByUserId(userId)); - LoginHelper.login(loginUser); - return StpUtil.getTokenValue(); + this.checkUserStatus(user); + return this.login(user); + } + + @Override + public String socialLogin(AuthUser authUser) { + String source = authUser.getSource(); + String openId = authUser.getUuid(); + UserSocialDO userSocial = userSocialService.getBySourceAndOpenId(source, openId); + UserDO user; + if (null == userSocial) { + String username = authUser.getUsername(); + boolean isMatch = ReUtil.isMatch(RegexConsts.USERNAME, username); + UserDO existsUser = userService.getByUsername(username); + if (null != existsUser || !isMatch) { + username = RandomUtil.randomString(RandomUtil.BASE_CHAR, 5) + IdUtil.fastSimpleUUID(); + } + user = new UserDO(); + user.setUsername(username); + user.setNickname(authUser.getNickname()); + user.setGender(GenderEnum.valueOf(authUser.getGender().name())); + user.setAvatar(authUser.getAvatar()); + user.setDeptId(SysConsts.SUPER_DEPT_ID); + Long userId = userService.save(user); + RoleDO role = roleService.getByCode(SysConsts.ADMIN_ROLE_CODE); + userRoleService.save(Collections.singletonList(role.getId()), userId); + userSocial = new UserSocialDO(); + userSocial.setUserId(userId); + userSocial.setSource(source); + userSocial.setOpenId(openId); + } else { + user = BeanUtil.toBean(userService.get(userSocial.getUserId()), UserDO.class); + } + this.checkUserStatus(user); + userSocial.setMetaJson(JSONUtil.toJsonStr(authUser)); + userSocial.setLastLoginTime(LocalDateTime.now()); + userSocialService.saveOrUpdate(userSocial); + return this.login(user); } @Override @@ -120,4 +157,32 @@ public class LoginServiceImpl implements LoginService { }); return BeanUtil.copyToList(treeList, RouteVO.class); } + + /** + * 登录并缓存用户信息 + * + * @param user + * 用户信息 + * @return 令牌 + */ + private String login(UserDO user) { + Long userId = user.getId(); + LoginUser loginUser = BeanUtil.copyProperties(user, LoginUser.class); + loginUser.setPermissions(permissionService.listPermissionByUserId(userId)); + loginUser.setRoleCodes(permissionService.listRoleCodeByUserId(userId)); + loginUser.setRoles(roleService.listByUserId(userId)); + return LoginHelper.login(loginUser); + } + + /** + * 检查用户状态 + * + * @param user + * 用户信息 + */ + private void checkUserStatus(UserDO user) { + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, user.getStatus(), "此账号已被禁用,如有疑问,请联系管理员"); + DeptDetailVO deptDetailVO = deptService.get(user.getDeptId()); + CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, deptDetailVO.getStatus(), "此账号部门已被禁用,如有疑问,请联系管理员"); + } } diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/mapper/UserSocialMapper.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/mapper/UserSocialMapper.java new file mode 100644 index 00000000..543a9374 --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/mapper/UserSocialMapper.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.system.mapper; + +import org.apache.ibatis.annotations.Param; + +import top.charles7c.cnadmin.common.base.BaseMapper; +import top.charles7c.cnadmin.system.model.entity.UserSocialDO; + +/** + * 用户社会化关联 Mapper + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +public interface UserSocialMapper extends BaseMapper { + + /** + * 根据来源和开放 ID 查询 + * + * @param source + * 来源 + * @param openId + * 开放 ID + * @return 用户社会化关联信息 + */ + UserSocialDO selectBySourceAndOpenId(@Param("source") String source, @Param("openId") String openId); +} diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/entity/UserSocialDO.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/entity/UserSocialDO.java new file mode 100644 index 00000000..be80832a --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/model/entity/UserSocialDO.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.system.model.entity; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import lombok.Data; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; + +/** + * 用户社会化关联实体 + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +@Data +@TableName("sys_user_social") +public class UserSocialDO implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 来源 + */ + private String source; + + /** + * 开放 ID + */ + private String openId; + + /** + * 附加信息 + */ + private String metaJson; + + /** + * 最后登录时间 + */ + private LocalDateTime lastLoginTime; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; +} diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleService.java index 8f1d0e72..e0365e14 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/RoleService.java @@ -22,6 +22,7 @@ import java.util.Set; import top.charles7c.cnadmin.common.base.BaseService; import top.charles7c.cnadmin.common.model.dto.RoleDTO; import top.charles7c.cnadmin.common.model.vo.LabelValueVO; +import top.charles7c.cnadmin.system.model.entity.RoleDO; import top.charles7c.cnadmin.system.model.query.RoleQuery; import top.charles7c.cnadmin.system.model.request.RoleRequest; import top.charles7c.cnadmin.system.model.vo.RoleDetailVO; @@ -70,4 +71,13 @@ public interface RoleService extends BaseService listByUserId(Long userId); + + /** + * 根据角色编码查询 + * + * @param code + * 角色编码 + * @return 角色信息 + */ + RoleDO getByCode(String code); } diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java index 17049ae3..d7376f9c 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserService.java @@ -37,6 +37,15 @@ import top.charles7c.cnadmin.system.model.vo.UserVO; */ public interface UserService extends BaseService { + /** + * 保存用户信息 + * + * @param user + * 用户信息 + * @return ID + */ + Long save(UserDO user); + /** * 上传头像 * diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserSocialService.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserSocialService.java new file mode 100644 index 00000000..b59e3162 --- /dev/null +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/UserSocialService.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.charles7c.cnadmin.system.service; + +import top.charles7c.cnadmin.system.model.entity.UserSocialDO; + +/** + * 用户社会化关联业务接口 + * + * @author Charles7c + * @since 2023/10/11 22:10 + */ +public interface UserSocialService { + + /** + * 根据来源和开放 ID 查询 + * + * @param source + * 来源 + * @param openId + * 开放 ID + * @return 用户社会化关联信息 + */ + UserSocialDO getBySourceAndOpenId(String source, String openId); + + /** + * 保存 + * + * @param userSocial + * 用户社会化关联信息 + */ + void saveOrUpdate(UserSocialDO userSocial); +} \ No newline at end of file diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java index d6a9010d..37ef7131 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/RoleServiceImpl.java @@ -178,6 +178,11 @@ public class RoleServiceImpl extends BaseServiceImpl(BeanUtil.copyToList(roleList, RoleDTO.class)); } + @Override + public RoleDO getByCode(String code) { + return baseMapper.lambdaQuery().eq(RoleDO::getCode, code).one(); + } + /** * 检查名称是否存在 * diff --git a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java index c3cd8142..ad6f51a5 100644 --- a/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java +++ b/continew-admin-system/src/main/java/top/charles7c/cnadmin/system/service/impl/UserServiceImpl.java @@ -18,9 +18,7 @@ package top.charles7c.cnadmin.system.service.impl; import java.io.File; import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Optional; +import java.util.*; import javax.annotation.Resource; @@ -82,6 +80,13 @@ public class UserServiceImpl extends BaseServiceImpl + + + + \ No newline at end of file diff --git a/continew-admin-ui/src/api/auth/login.ts b/continew-admin-ui/src/api/auth/login.ts index c5b37de6..d489fac5 100644 --- a/continew-admin-ui/src/api/auth/login.ts +++ b/continew-admin-ui/src/api/auth/login.ts @@ -32,3 +32,11 @@ export function getUserInfo() { export function listRoute() { return axios.get(`${BASE_URL}/route`); } + +export function socialAuth(source: string) { + return axios.get(`${BASE_URL}/${source}`); +} + +export function socialLogin(source: string, req: any) { + return axios.post(`${BASE_URL}/${source}`, req); +} diff --git a/continew-admin-ui/src/router/constants.ts b/continew-admin-ui/src/router/constants.ts index 7daef7f7..cb60bb2c 100644 --- a/continew-admin-ui/src/router/constants.ts +++ b/continew-admin-ui/src/router/constants.ts @@ -1,6 +1,7 @@ export const WHITE_LIST = [ { name: 'notFound', children: [] }, { name: 'login', children: [] }, + { name: 'SocialCallback', children: [] }, ]; export const NOT_FOUND = { diff --git a/continew-admin-ui/src/router/guard/userLoginInfo.ts b/continew-admin-ui/src/router/guard/userLoginInfo.ts index cd65864e..aa39b721 100644 --- a/continew-admin-ui/src/router/guard/userLoginInfo.ts +++ b/continew-admin-ui/src/router/guard/userLoginInfo.ts @@ -29,7 +29,7 @@ export default function setupUserLoginInfoGuard(router: Router) { } } } else { - if (to.name === 'login') { + if (to.name === 'login' || to.name === 'SocialCallback') { next(); return; } diff --git a/continew-admin-ui/src/router/index.ts b/continew-admin-ui/src/router/index.ts index 4347a956..d1db02cd 100644 --- a/continew-admin-ui/src/router/index.ts +++ b/continew-admin-ui/src/router/index.ts @@ -29,6 +29,14 @@ const router = createRouter({ requiresAuth: false, }, }, + { + path: '/social/callback', + name: 'SocialCallback', + component: () => import('@/views/login/social/index.vue'), + meta: { + requiresAuth: false, + }, + }, ...appRoutes, ...fixedRoutes, ...demoRoutes, diff --git a/continew-admin-ui/src/store/modules/login/index.ts b/continew-admin-ui/src/store/modules/login/index.ts index bdbf81dc..4d3d68a8 100644 --- a/continew-admin-ui/src/store/modules/login/index.ts +++ b/continew-admin-ui/src/store/modules/login/index.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia'; import { login as userLogin, + socialLogin as userSocialLogin, logout as userLogout, getUserInfo, LoginReq, @@ -52,6 +53,17 @@ const useLoginStore = defineStore('user', { } }, + // 社交身份登录 + async socialLogin(source: string, req: any) { + try { + const res = await userSocialLogin(source, req); + setToken(res.data.token); + } catch (err) { + clearToken(); + throw err; + } + }, + // 用户退出 async logout() { try { diff --git a/continew-admin-ui/src/utils/avatar.ts b/continew-admin-ui/src/utils/avatar.ts index 5f0e609c..5dea2cb3 100644 --- a/continew-admin-ui/src/utils/avatar.ts +++ b/continew-admin-ui/src/utils/avatar.ts @@ -8,7 +8,14 @@ export default function getAvatar( ) { if (avatar) { const baseUrl = import.meta.env.VITE_API_BASE_URL; - return `${baseUrl}/avatar/${avatar}`; + if ( + !avatar.startsWith('http://') && + !avatar.startsWith('https://') && + !avatar.startsWith('blob:') + ) { + return `${baseUrl}/avatar/${avatar}`; + } + return avatar; } if (gender === 1) { diff --git a/continew-admin-ui/src/views/login/index.vue b/continew-admin-ui/src/views/login/index.vue index 58a14058..dc49cece 100644 --- a/continew-admin-ui/src/views/login/index.vue +++ b/continew-admin-ui/src/views/login/index.vue @@ -11,21 +11,15 @@
@@ -87,6 +81,7 @@ import { useAppStore } from '@/store'; import getFile from '@/utils/file'; import useResponsive from '@/hooks/responsive'; + import { socialAuth } from '@/api/auth/login'; import AccountLogin from './components/account-login.vue'; import PhoneLogin from './components/phone-login.vue'; import EmailLogin from './components/email-login.vue'; @@ -96,6 +91,16 @@ useResponsive(true); const isEmailLogin = ref(false); + /** + * 第三方登录授权 + * + * @param source 来源 + */ + const handleSocialAuth = async (source: string) => { + const { data } = await socialAuth(source); + window.location.href = data; + }; + const toggleLoginMode = () => { isEmailLogin.value = !isEmailLogin.value; }; diff --git a/continew-admin-ui/src/views/login/locale/en-US.ts b/continew-admin-ui/src/views/login/locale/en-US.ts index 2b895280..e3de6d63 100644 --- a/continew-admin-ui/src/views/login/locale/en-US.ts +++ b/continew-admin-ui/src/views/login/locale/en-US.ts @@ -4,6 +4,7 @@ export default { 'login.phone': 'Phone Login', 'login.email': 'Email Login', 'login.other': 'Other Login', + 'login.ing': 'Login...', 'login.account.placeholder.username': 'Please enter username', 'login.account.placeholder.password': 'Please enter password', diff --git a/continew-admin-ui/src/views/login/locale/zh-CN.ts b/continew-admin-ui/src/views/login/locale/zh-CN.ts index 4d377517..bd07c9c1 100644 --- a/continew-admin-ui/src/views/login/locale/zh-CN.ts +++ b/continew-admin-ui/src/views/login/locale/zh-CN.ts @@ -4,6 +4,7 @@ export default { 'login.phone': '手机号登录', 'login.email': '邮箱登录', 'login.other': '其他登录方式', + 'login.ing': '登录中...', 'login.account.placeholder.username': '请输入用户名', 'login.account.placeholder.password': '请输入密码', diff --git a/continew-admin-ui/src/views/login/social/index.vue b/continew-admin-ui/src/views/login/social/index.vue new file mode 100644 index 00000000..3e8342b0 --- /dev/null +++ b/continew-admin-ui/src/views/login/social/index.vue @@ -0,0 +1,67 @@ + + + + + + + diff --git a/continew-admin-ui/src/views/system/user/index.vue b/continew-admin-ui/src/views/system/user/index.vue index 0d808b38..ca7da39f 100644 --- a/continew-admin-ui/src/views/system/user/index.vue +++ b/continew-admin-ui/src/views/system/user/index.vue @@ -147,7 +147,7 @@ >