Commit 133cb49c42cd22ce71ddf51bb8288fc69558a8fe
1 parent
c588a4d9
接入第三方登录(已接入Gitee[理论justAuth支持都可以接入]--抢先预览版
Showing
27 changed files
with
1049 additions
and
44 deletions
sql/ruoyi-vue-pro.sql
| ... | ... | @@ -2426,3 +2426,21 @@ INSERT INTO `tool_test_demo` VALUES (107, '哈哈哈哈', 1, 0, 1, 'biubiubui', |
| 2426 | 2426 | COMMIT; |
| 2427 | 2427 | |
| 2428 | 2428 | SET FOREIGN_KEY_CHECKS = 1; |
| 2429 | + | |
| 2430 | +-- ---------------------------- | |
| 2431 | +-- Table structure for sys_user_social | |
| 2432 | +-- ---------------------------- | |
| 2433 | +DROP TABLE IF EXISTS `sys_user_social`; | |
| 2434 | +CREATE TABLE `sys_user_social` ( | |
| 2435 | + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键(自增策略)', | |
| 2436 | + `user_id` bigint NOT NULL COMMENT '系统用户ID', | |
| 2437 | + `social_user_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '第三用户ID', | |
| 2438 | + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '创建者', | |
| 2439 | + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', | |
| 2440 | + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '更新者', | |
| 2441 | + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', | |
| 2442 | + `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除', | |
| 2443 | + PRIMARY KEY (`id`) USING BTREE | |
| 2444 | +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC; | |
| 2445 | + | |
| 2446 | +SET FOREIGN_KEY_CHECKS = 1; | |
| 2429 | 2447 | \ No newline at end of file | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java
| 1 | 1 | package cn.iocoder.yudao.adminserver.modules.system.controller.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysUserSessionService; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
| ... | ... | @@ -19,6 +20,8 @@ import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; |
| 19 | 20 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; |
| 20 | 21 | import io.swagger.annotations.Api; |
| 21 | 22 | import io.swagger.annotations.ApiOperation; |
| 23 | +import org.springframework.security.core.Authentication; | |
| 24 | +import org.springframework.security.core.context.SecurityContextHolder; | |
| 22 | 25 | import org.springframework.validation.annotation.Validated; |
| 23 | 26 | import org.springframework.web.bind.annotation.*; |
| 24 | 27 | |
| ... | ... | @@ -46,6 +49,8 @@ public class SysAuthController { |
| 46 | 49 | private SysRoleService roleService; |
| 47 | 50 | @Resource |
| 48 | 51 | private SysPermissionService permissionService; |
| 52 | + @Resource | |
| 53 | + private SysUserSessionService sysUserSessionService; | |
| 49 | 54 | |
| 50 | 55 | @PostMapping("/login") |
| 51 | 56 | @ApiOperation("使用账号密码登录") |
| ... | ... | @@ -56,6 +61,17 @@ public class SysAuthController { |
| 56 | 61 | return success(SysAuthLoginRespVO.builder().token(token).build()); |
| 57 | 62 | } |
| 58 | 63 | |
| 64 | + @RequestMapping("/auth2/login/{oauthType}") | |
| 65 | + @ApiOperation("第三方登录") | |
| 66 | + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 67 | + public CommonResult<SysAuthLoginRespVO> login(@PathVariable String oauthType) { | |
| 68 | + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | |
| 69 | + //TODO NPE | |
| 70 | + String token = sysUserSessionService.getSessionId(authentication.getName()); | |
| 71 | + // 返回结果 | |
| 72 | + return success(SysAuthLoginRespVO.builder().token(token).build()); | |
| 73 | + } | |
| 74 | + | |
| 59 | 75 | @GetMapping("/get-permission-info") |
| 60 | 76 | @ApiOperation("获取登陆用户的权限信息") |
| 61 | 77 | public CommonResult<SysAuthPermissionInfoRespVO> getPermissionInfo() { | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/user/vo/user/AuthUser.java
0 → 100644
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/convert/auth/SysAuthConvert.java
| 1 | 1 | package cn.iocoder.yudao.adminserver.modules.system.convert.auth; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserCreateReqVO; | |
| 4 | +import cn.iocoder.yudao.adminserver.modules.system.enums.common.SysSexEnum; | |
| 5 | +import cn.iocoder.yudao.framework.security.core.Auth2LoginUser; | |
| 3 | 6 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 4 | 7 | import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthMenuRespVO; |
| 5 | 8 | import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthPermissionInfoRespVO; |
| ... | ... | @@ -10,8 +13,9 @@ import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.Sys |
| 10 | 13 | import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO; |
| 11 | 14 | import cn.iocoder.yudao.adminserver.modules.system.enums.permission.MenuIdEnum; |
| 12 | 15 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 13 | -import org.mapstruct.Mapper; | |
| 14 | -import org.mapstruct.Mapping; | |
| 16 | +import me.zhyd.oauth.enums.AuthUserGender; | |
| 17 | +import me.zhyd.oauth.model.AuthUser; | |
| 18 | +import org.mapstruct.*; | |
| 15 | 19 | import org.mapstruct.factory.Mappers; |
| 16 | 20 | import org.slf4j.LoggerFactory; |
| 17 | 21 | |
| ... | ... | @@ -21,7 +25,7 @@ import java.util.LinkedHashMap; |
| 21 | 25 | import java.util.List; |
| 22 | 26 | import java.util.Map; |
| 23 | 27 | |
| 24 | -@Mapper | |
| 28 | +@Mapper(uses = SysAuthConvert.UserSexTransform.class) | |
| 25 | 29 | public interface SysAuthConvert { |
| 26 | 30 | |
| 27 | 31 | SysAuthConvert INSTANCE = Mappers.getMapper(SysAuthConvert.class); |
| ... | ... | @@ -29,6 +33,7 @@ public interface SysAuthConvert { |
| 29 | 33 | @Mapping(source = "updateTime", target = "updateTime", ignore = true) |
| 30 | 34 | // 字段相同,但是含义不同,忽略 |
| 31 | 35 | LoginUser convert(SysUserDO bean); |
| 36 | + Auth2LoginUser getAuth2LoginUser(SysUserDO bean); | |
| 32 | 37 | |
| 33 | 38 | default SysAuthPermissionInfoRespVO convert(SysUserDO user, List<SysRoleDO> roleList, List<SysMenuDO> menuList) { |
| 34 | 39 | return SysAuthPermissionInfoRespVO.builder() |
| ... | ... | @@ -44,6 +49,16 @@ public interface SysAuthConvert { |
| 44 | 49 | |
| 45 | 50 | LoginUser convert(SysUserProfileUpdatePasswordReqVO reqVO); |
| 46 | 51 | |
| 52 | + @Mappings( | |
| 53 | + @Mapping(target = "sex", source = "gender") | |
| 54 | + ) | |
| 55 | + SysUserCreateReqVO convert(AuthUser authUser); | |
| 56 | + | |
| 57 | + @Mappings( | |
| 58 | + @Mapping(target = "thirdPartyUserId", source = "uuid") | |
| 59 | + ) | |
| 60 | + Auth2LoginUser getLoginUser(AuthUser authUser); | |
| 61 | + | |
| 47 | 62 | /** |
| 48 | 63 | * 将菜单列表,构建成菜单树 |
| 49 | 64 | * |
| ... | ... | @@ -76,4 +91,24 @@ public interface SysAuthConvert { |
| 76 | 91 | return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId())); |
| 77 | 92 | } |
| 78 | 93 | |
| 94 | + public class UserSexTransform { | |
| 95 | + | |
| 96 | + public int toInt (AuthUserGender gender){ | |
| 97 | + switch (gender) { | |
| 98 | + case MALE: | |
| 99 | + return SysSexEnum.MALE.getSex(); | |
| 100 | + case FEMALE: | |
| 101 | + return SysSexEnum.FEMALE.getSex(); | |
| 102 | + default: | |
| 103 | + return SysSexEnum.UNKNOWN.getSex(); | |
| 104 | + } | |
| 105 | + } | |
| 106 | + | |
| 107 | + public AuthUserGender strToBoolean(int sex){ | |
| 108 | + if(sex == SysSexEnum.UNKNOWN.getSex()) { | |
| 109 | + return AuthUserGender.UNKNOWN; | |
| 110 | + } | |
| 111 | + return AuthUserGender.getRealGender(String.valueOf(sex)); | |
| 112 | + } | |
| 113 | + } | |
| 79 | 114 | } | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/convert/auth/config/AuthConfig.java
0 → 100644
| 1 | +package cn.iocoder.yudao.adminserver.modules.system.convert.auth.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.adminserver.modules.system.convert.auth.handler.DefaultSignUpUrlAuthenticationSuccessHandler; | |
| 4 | +import cn.iocoder.yudao.framework.security.core.handler.AbstractSignUpUrlAuthenticationSuccessHandler; | |
| 5 | +import org.springframework.context.annotation.Bean; | |
| 6 | +import org.springframework.context.annotation.Configuration; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * @author weir | |
| 10 | + */ | |
| 11 | +@Configuration | |
| 12 | +public class AuthConfig { | |
| 13 | + @Bean | |
| 14 | + public AbstractSignUpUrlAuthenticationSuccessHandler authenticationSuccessHandler() { | |
| 15 | + AbstractSignUpUrlAuthenticationSuccessHandler successHandler = new DefaultSignUpUrlAuthenticationSuccessHandler(); | |
| 16 | + successHandler.setDefaultTargetUrl("/api/callback"); | |
| 17 | + return successHandler; | |
| 18 | + } | |
| 19 | +} | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/convert/auth/handler/DefaultSignUpUrlAuthenticationSuccessHandler.java
0 → 100644
| 1 | +/* | |
| 2 | + * MIT License | |
| 3 | + * Copyright (c) 2020-2029 YongWu zheng (dcenter.top and gitee.com/pcore and github.com/ZeroOrInfinity) | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | + * of this software and associated documentation files (the "Software"), to deal | |
| 7 | + * in the Software without restriction, including without limitation the rights | |
| 8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | + * copies of the Software, and to permit persons to whom the Software is | |
| 10 | + * furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in all | |
| 13 | + * copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | + * SOFTWARE. | |
| 22 | + */ | |
| 23 | +package cn.iocoder.yudao.adminserver.modules.system.convert.auth.handler; | |
| 24 | + | |
| 25 | +import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginRespVO; | |
| 26 | +import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysUserSessionService; | |
| 27 | +import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; | |
| 28 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 29 | +import cn.iocoder.yudao.framework.security.core.Auth2LoginUser; | |
| 30 | +import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 31 | +import cn.iocoder.yudao.framework.security.core.handler.AbstractSignUpUrlAuthenticationSuccessHandler; | |
| 32 | +import org.apache.commons.lang3.StringUtils; | |
| 33 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 34 | +import org.springframework.http.HttpStatus; | |
| 35 | +import org.springframework.security.core.Authentication; | |
| 36 | +import org.springframework.security.core.context.SecurityContextHolder; | |
| 37 | +import org.springframework.security.web.DefaultRedirectStrategy; | |
| 38 | +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; | |
| 39 | +import org.springframework.security.web.savedrequest.RequestCache; | |
| 40 | + | |
| 41 | +import javax.annotation.Resource; | |
| 42 | +import javax.servlet.ServletException; | |
| 43 | +import javax.servlet.http.HttpServletRequest; | |
| 44 | +import javax.servlet.http.HttpServletResponse; | |
| 45 | +import java.io.IOException; | |
| 46 | +import java.util.Set; | |
| 47 | + | |
| 48 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 49 | +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | |
| 50 | +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; | |
| 51 | +import static java.util.Collections.singleton; | |
| 52 | +import static top.dcenter.ums.security.core.oauth.util.MvcUtil.*; | |
| 53 | + | |
| 54 | +/** | |
| 55 | + * @author weir | |
| 56 | + */ | |
| 57 | +public class DefaultSignUpUrlAuthenticationSuccessHandler extends AbstractSignUpUrlAuthenticationSuccessHandler { | |
| 58 | + private RequestCache requestCache = new HttpSessionRequestCache(); | |
| 59 | + @Autowired | |
| 60 | + private SysUserSessionService userSessionService; | |
| 61 | + @Resource | |
| 62 | + private SysPermissionService permissionService; | |
| 63 | + | |
| 64 | + @Override | |
| 65 | + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { | |
| 66 | + final Object principal = authentication.getPrincipal(); | |
| 67 | + String token = userSessionService.createUserSession(defaultHandleUserRoles((LoginUser) principal), getClientIP(), getUserAgent()); | |
| 68 | + if(StringUtils.isNotBlank(token)) { | |
| 69 | + SecurityContextHolder.getContext().setAuthentication(authentication); | |
| 70 | + } | |
| 71 | + if (principal instanceof Auth2LoginUser) { | |
| 72 | + new DefaultRedirectStrategy().sendRedirect(request, response, getUrl() + token); | |
| 73 | + return; | |
| 74 | + } | |
| 75 | + if (isAjaxOrJson(request)) { | |
| 76 | + responseWithJson(response, HttpStatus.OK.value(), toJsonString(success(SysAuthLoginRespVO.builder().token(token).build()))); | |
| 77 | + return; | |
| 78 | + } | |
| 79 | + try { | |
| 80 | + requestCache.saveRequest(request, response); | |
| 81 | + super.setRequestCache(requestCache); | |
| 82 | + super.onAuthenticationSuccess(request, response, authentication); | |
| 83 | + } catch (ServletException e) { | |
| 84 | + e.printStackTrace(); | |
| 85 | + } | |
| 86 | + | |
| 87 | + } | |
| 88 | + | |
| 89 | + private String getUrl() { | |
| 90 | + return "http://localhost/oauthLogin/gitee?token="; | |
| 91 | + } | |
| 92 | + | |
| 93 | + /** | |
| 94 | + * 默认处理方式处理用户角色列表;建议角色权限前置到 UserDetails | |
| 95 | + * | |
| 96 | + * @param loginUser 用户 | |
| 97 | + * @return | |
| 98 | + */ | |
| 99 | + private LoginUser defaultHandleUserRoles(LoginUser loginUser) { | |
| 100 | + Set<Long> roleIds = loginUser.getRoleIds(); | |
| 101 | + if (roleIds == null || roleIds.isEmpty()) { | |
| 102 | + Set<Long> userRoleIds = permissionService.getUserRoleIds(loginUser.getId(), singleton(CommonStatusEnum.ENABLE.getStatus())); | |
| 103 | + loginUser.setRoleIds(userRoleIds); | |
| 104 | + } | |
| 105 | + return loginUser; | |
| 106 | + } | |
| 107 | + | |
| 108 | + @Override | |
| 109 | + public void setRequestCache(RequestCache requestCache) { | |
| 110 | + this.requestCache = requestCache; | |
| 111 | + } | |
| 112 | +} | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SocialUserDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 4 | +import lombok.Data; | |
| 5 | +import me.zhyd.oauth.model.AuthUser; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * @author weir | |
| 9 | + */ | |
| 10 | +@Data | |
| 11 | +@TableName("user_connection") | |
| 12 | +public class SocialUserDO extends AuthUser { | |
| 13 | +} | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SysUserSocialDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | |
| 4 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 5 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.EqualsAndHashCode; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 系统用户和第三方用户关联表 | |
| 11 | + * @author weir | |
| 12 | + */ | |
| 13 | +@TableName("sys_user_social") | |
| 14 | +@Data | |
| 15 | +public class SysUserSocialDO extends BaseDO { | |
| 16 | + /** | |
| 17 | + * 自增主键 | |
| 18 | + */ | |
| 19 | + @TableId | |
| 20 | + private Long id; | |
| 21 | + /** | |
| 22 | + * 用户 ID | |
| 23 | + */ | |
| 24 | + private Long userId; | |
| 25 | + /** | |
| 26 | + * 角色 ID | |
| 27 | + */ | |
| 28 | + private String socialUserId; | |
| 29 | +} | |
| 30 | + | |
| 31 | + | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSysUserSocialMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysUserSocialDO; | |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | |
| 5 | +import org.apache.ibatis.annotations.Mapper; | |
| 6 | + | |
| 7 | +@Mapper | |
| 8 | +public interface SysSysUserSocialMapper extends BaseMapperX<SysUserSocialDO> { | |
| 9 | + | |
| 10 | + | |
| 11 | +} | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysUserSocialMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysUserSocialDO; | |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | |
| 5 | +import org.apache.ibatis.annotations.Mapper; | |
| 6 | + | |
| 7 | +@Mapper | |
| 8 | +public interface SysUserSocialMapper extends BaseMapperX<SysUserSocialDO> { | |
| 9 | + | |
| 10 | + | |
| 11 | +} | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/common/SysSexEnum.java
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysUserSessionService.java
| ... | ... | @@ -46,6 +46,13 @@ public interface SysUserSessionService { |
| 46 | 46 | LoginUser getLoginUser(String sessionId); |
| 47 | 47 | |
| 48 | 48 | /** |
| 49 | + * 获取当前登录用户信息 | |
| 50 | + * @param username 用户名称 | |
| 51 | + * @return 在线用户 | |
| 52 | + */ | |
| 53 | + String getSessionId(String username); | |
| 54 | + | |
| 55 | + /** | |
| 49 | 56 | * 获得 Session 超时时间,单位:毫秒 |
| 50 | 57 | * |
| 51 | 58 | * @return 超时时间 | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysUserSessionServiceImpl.java
| ... | ... | @@ -18,7 +18,11 @@ import cn.iocoder.yudao.adminserver.modules.system.enums.logger.SysLoginResultEn |
| 18 | 18 | import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysUserSessionService; |
| 19 | 19 | import cn.iocoder.yudao.adminserver.modules.system.service.logger.SysLoginLogService; |
| 20 | 20 | import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; |
| 21 | +import com.baomidou.mybatisplus.core.conditions.Wrapper; | |
| 22 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 23 | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |
| 21 | 24 | import com.google.common.collect.Lists; |
| 25 | +import lombok.extern.slf4j.Slf4j; | |
| 22 | 26 | import org.springframework.stereotype.Service; |
| 23 | 27 | |
| 24 | 28 | import javax.annotation.Resource; |
| ... | ... | @@ -34,6 +38,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; |
| 34 | 38 | * |
| 35 | 39 | * @author 芋道源码 |
| 36 | 40 | */ |
| 41 | +@Slf4j | |
| 37 | 42 | @Service |
| 38 | 43 | public class SysUserSessionServiceImpl implements SysUserSessionService { |
| 39 | 44 | |
| ... | ... | @@ -92,6 +97,15 @@ public class SysUserSessionServiceImpl implements SysUserSessionService { |
| 92 | 97 | } |
| 93 | 98 | |
| 94 | 99 | @Override |
| 100 | + public String getSessionId(String username) { | |
| 101 | + QueryWrapper<SysUserSessionDO> wrapper = new QueryWrapper<>(); | |
| 102 | + wrapper.eq("username", username); | |
| 103 | + wrapper.orderByDesc("create_time"); | |
| 104 | + SysUserSessionDO sysUserSessionDO = userSessionMapper.selectOne(wrapper); | |
| 105 | + return sysUserSessionDO.getId(); | |
| 106 | + } | |
| 107 | + | |
| 108 | + @Override | |
| 95 | 109 | public Long getSessionTimeoutMillis() { |
| 96 | 110 | return securityProperties.getSessionTimeout().toMillis(); |
| 97 | 111 | } | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/UserDetailsServiceImpl.java
0 → 100644
| 1 | +/* | |
| 2 | + * MIT License | |
| 3 | + * Copyright (c) 2020-2029 YongWu zheng (dcenter.top and gitee.com/pcore and github.com/ZeroOrInfinity) | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | + * of this software and associated documentation files (the "Software"), to deal | |
| 7 | + * in the Software without restriction, including without limitation the rights | |
| 8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | + * copies of the Software, and to permit persons to whom the Software is | |
| 10 | + * furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in all | |
| 13 | + * copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | + * SOFTWARE. | |
| 22 | + */ | |
| 23 | + | |
| 24 | +package cn.iocoder.yudao.adminserver.modules.system.service.auth.impl; | |
| 25 | + | |
| 26 | +import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserCreateReqVO; | |
| 27 | +import cn.iocoder.yudao.adminserver.modules.system.convert.auth.SysAuthConvert; | |
| 28 | +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO; | |
| 29 | +import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; | |
| 30 | +import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; | |
| 31 | +import cn.iocoder.yudao.framework.security.core.Auth2LoginUser; | |
| 32 | +import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 33 | +import com.google.common.collect.Sets; | |
| 34 | +import me.zhyd.oauth.model.AuthUser; | |
| 35 | +import org.apache.commons.lang3.StringUtils; | |
| 36 | +import org.slf4j.Logger; | |
| 37 | +import org.slf4j.LoggerFactory; | |
| 38 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 39 | +import org.springframework.lang.NonNull; | |
| 40 | +import org.springframework.security.core.authority.AuthorityUtils; | |
| 41 | +import org.springframework.security.core.userdetails.User; | |
| 42 | +import org.springframework.security.core.userdetails.UserCache; | |
| 43 | +import org.springframework.security.core.userdetails.UserDetails; | |
| 44 | +import org.springframework.security.core.userdetails.UsernameNotFoundException; | |
| 45 | +import org.springframework.security.crypto.password.PasswordEncoder; | |
| 46 | +import org.springframework.stereotype.Service; | |
| 47 | +import org.springframework.web.context.request.RequestAttributes; | |
| 48 | +import org.springframework.web.context.request.RequestContextHolder; | |
| 49 | +import top.dcenter.ums.security.core.oauth.enums.ErrorCodeEnum; | |
| 50 | +import top.dcenter.ums.security.core.oauth.exception.RegisterUserFailureException; | |
| 51 | +import top.dcenter.ums.security.core.oauth.exception.UserNotExistException; | |
| 52 | +import top.dcenter.ums.security.core.oauth.service.UmsUserDetailsService; | |
| 53 | + | |
| 54 | +import javax.annotation.Resource; | |
| 55 | +import java.util.ArrayList; | |
| 56 | +import java.util.List; | |
| 57 | +import java.util.Map; | |
| 58 | +import java.util.Set; | |
| 59 | + | |
| 60 | +/** | |
| 61 | + * 用户密码与手机短信登录与注册服务:<br><br> | |
| 62 | + * 1. 用于第三方登录与手机短信登录逻辑。<br><br> | |
| 63 | + * 2. 用于用户密码登录逻辑。<br><br> | |
| 64 | + * 3. 用户注册逻辑。<br><br> | |
| 65 | + * @author YongWu zheng | |
| 66 | + * @version V1.0 Created by 2020/9/20 11:06 | |
| 67 | + */ | |
| 68 | +@Service | |
| 69 | +public class UserDetailsServiceImpl implements UmsUserDetailsService { | |
| 70 | + | |
| 71 | + private final Logger log = LoggerFactory.getLogger(this.getClass()); | |
| 72 | + | |
| 73 | + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") | |
| 74 | + @Autowired(required = false) | |
| 75 | + private UserCache userCache; | |
| 76 | + | |
| 77 | + @Resource | |
| 78 | + private SysUserService userService; | |
| 79 | + | |
| 80 | + /** | |
| 81 | + * 用于密码加解密 | |
| 82 | + */ | |
| 83 | + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") | |
| 84 | + @Autowired | |
| 85 | + private PasswordEncoder passwordEncoder; | |
| 86 | + @Autowired | |
| 87 | + private SysPermissionService permissionService; | |
| 88 | + | |
| 89 | + @SuppressWarnings("AlibabaUndefineMagicConstant") | |
| 90 | + @Override | |
| 91 | + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |
| 92 | + | |
| 93 | + try | |
| 94 | + { | |
| 95 | + // 从缓存中查询用户信息: | |
| 96 | + // 从缓存中查询用户信息 | |
| 97 | + if (this.userCache != null) | |
| 98 | + { | |
| 99 | + UserDetails userDetails = this.userCache.getUserFromCache(username); | |
| 100 | + if (userDetails != null) | |
| 101 | + { | |
| 102 | + return userDetails; | |
| 103 | + } | |
| 104 | + } | |
| 105 | + // 根据用户名获取用户信息 | |
| 106 | + // 获取 username 对应的 SysUserDO | |
| 107 | + SysUserDO user = userService.getUserByUsername(username); | |
| 108 | + if (user == null) { | |
| 109 | + throw new UsernameNotFoundException(username); | |
| 110 | + } | |
| 111 | + // 创建 LoginUser 对象 | |
| 112 | + Auth2LoginUser loginUser = SysAuthConvert.INSTANCE.getAuth2LoginUser(user); | |
| 113 | + //TODO 登录日志等可以和用户名密码等兼容处理 | |
| 114 | + return loginUser; | |
| 115 | + } | |
| 116 | + catch (Exception e) | |
| 117 | + { | |
| 118 | + String msg = String.format("第三方登录 ======>: 登录用户名:%s, 登录失败: %s", username, e.getMessage()); | |
| 119 | + log.error(msg); | |
| 120 | + throw new UserNotExistException(ErrorCodeEnum.QUERY_USER_INFO_ERROR, e, username); | |
| 121 | + } | |
| 122 | + } | |
| 123 | + | |
| 124 | + @Override | |
| 125 | + public UserDetails registerUser(@NonNull AuthUser authUser, @NonNull String username, | |
| 126 | + @NonNull String defaultAuthority, String decodeState) throws RegisterUserFailureException { | |
| 127 | + | |
| 128 | + // 这里的 decodeState 可以根据自己实现的 top.dcenter.ums.security.core.oauth.service.Auth2StateCoder 接口的逻辑来传递必要的参数. | |
| 129 | + // 比如: 第三方登录成功后的跳转地址 | |
| 130 | + final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); | |
| 131 | + // 假设 decodeState 就是 redirectUrl, 我们直接把 redirectUrl 设置到 request 上 | |
| 132 | + // 后续经过成功处理器时直接从 requestAttributes.getAttribute("redirectUrl", RequestAttributes.SCOPE_REQUEST) 获取并跳转 | |
| 133 | + if (requestAttributes != null) { | |
| 134 | + requestAttributes.setAttribute("redirectUrl", decodeState, RequestAttributes.SCOPE_REQUEST); | |
| 135 | + } | |
| 136 | + //返回用户 | |
| 137 | + LoginUser loginUser = doRegistUser(authUser); | |
| 138 | + | |
| 139 | + log.info("第三方用户注册 ======>: 用户名:{}, 注册成功", username); | |
| 140 | + | |
| 141 | + // 把用户信息存入缓存 | |
| 142 | + if (userCache != null) | |
| 143 | + { | |
| 144 | + userCache.putUserInCache(loginUser); | |
| 145 | + } | |
| 146 | + | |
| 147 | + return loginUser; | |
| 148 | + } | |
| 149 | + | |
| 150 | + private LoginUser doRegistUser(AuthUser authUser) { | |
| 151 | + SysUserCreateReqVO reqVO = SysAuthConvert.INSTANCE.convert(authUser); | |
| 152 | + if (StringUtils.isEmpty(reqVO.getPassword())) { | |
| 153 | + reqVO.setPassword(getDefaultPassword()); | |
| 154 | + } | |
| 155 | + //添加用户 | |
| 156 | + Long sysUserId = userService.createUser(reqVO); | |
| 157 | + //关联第三方用户 | |
| 158 | + Long userId = userService.bindSocialUSer(sysUserId, authUser.getUuid()); | |
| 159 | + //赋予默认角色权限;三方登录默认部分 | |
| 160 | + permissionService.assignUserRole(userId, getDefaultRoles()); | |
| 161 | + LoginUser loginUser = SysAuthConvert.INSTANCE.getLoginUser(authUser); | |
| 162 | + loginUser.setRoleIds(getDefaultRoles()); | |
| 163 | + loginUser.setPassword(getDefaultPassword()); | |
| 164 | + loginUser.setId(sysUserId); | |
| 165 | + return loginUser; | |
| 166 | + } | |
| 167 | + | |
| 168 | + private String getDefaultPassword() { | |
| 169 | + return "123456"; | |
| 170 | + } | |
| 171 | + | |
| 172 | + protected Set<Long> getDefaultRoles() { | |
| 173 | + return Sets.newHashSet(1L); | |
| 174 | + } | |
| 175 | + | |
| 176 | + @NonNull | |
| 177 | + public UserDetails registerUser(@NonNull String mobile, Map<String, String> otherParamMap) throws RegisterUserFailureException { | |
| 178 | + | |
| 179 | + // 用户信息持久化逻辑。。。 | |
| 180 | + // ... | |
| 181 | + | |
| 182 | + log.info("Demo ======>: 手机短信登录用户 {}:注册成功", mobile); | |
| 183 | + | |
| 184 | + User user = new User(mobile, | |
| 185 | + passwordEncoder.encode("admin"), | |
| 186 | + true, | |
| 187 | + true, | |
| 188 | + true, | |
| 189 | + true, | |
| 190 | + AuthorityUtils.commaSeparatedStringToAuthorityList("admin, ROLE_USER") | |
| 191 | + ); | |
| 192 | + | |
| 193 | + // 把用户信息存入缓存 | |
| 194 | + if (userCache != null) | |
| 195 | + { | |
| 196 | + userCache.putUserInCache(user); | |
| 197 | + } | |
| 198 | + | |
| 199 | + return user; | |
| 200 | + } | |
| 201 | + | |
| 202 | + /** | |
| 203 | + * {@link #existedByUsernames(String...)} usernames 生成规则. | |
| 204 | + * 如需自定义重新实现此逻辑 | |
| 205 | + * @param authUser 第三方用户信息 | |
| 206 | + * @return 返回一个 username 数组 | |
| 207 | + */ | |
| 208 | + @Override | |
| 209 | + public String[] generateUsernames(AuthUser authUser) { | |
| 210 | + return new String[]{ | |
| 211 | + authUser.getUsername(), | |
| 212 | + // providerId = authUser.getSource() | |
| 213 | + authUser.getUsername() + "_" + authUser.getSource(), | |
| 214 | + // providerUserId = authUser.getUuid() | |
| 215 | + authUser.getUsername() + "_" + authUser.getSource() + "_" + authUser.getUuid() | |
| 216 | + }; | |
| 217 | + } | |
| 218 | + | |
| 219 | + @Override | |
| 220 | + public UserDetails loadUserByUserId(String userId) throws UsernameNotFoundException { | |
| 221 | + UserDetails userDetails = loadUserByUsername(userId); | |
| 222 | + User.withUserDetails(userDetails); | |
| 223 | + return userDetails; | |
| 224 | + } | |
| 225 | + | |
| 226 | + @Override | |
| 227 | + public List<Boolean> existedByUsernames(String... usernames) throws UsernameNotFoundException { | |
| 228 | + // ... 在本地账户上查询 userIds 是否已被使用 | |
| 229 | + List<Boolean> list = new ArrayList<>(); | |
| 230 | + for (String username : usernames) { | |
| 231 | + SysUserDO userDO = userService.getUserByUsername(username); | |
| 232 | + list.add(userDO != null); | |
| 233 | + } | |
| 234 | + return list; | |
| 235 | + } | |
| 236 | + | |
| 237 | +} | |
| 0 | 238 | \ No newline at end of file | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java
| ... | ... | @@ -35,6 +35,14 @@ public interface SysUserService { |
| 35 | 35 | Long createUser(SysUserCreateReqVO reqVO); |
| 36 | 36 | |
| 37 | 37 | /** |
| 38 | + * 绑定第三方用户 | |
| 39 | + * @param sysUserId 系统用户ID | |
| 40 | + * @param socialUSerId 第三方唯一标识 | |
| 41 | + * @return | |
| 42 | + */ | |
| 43 | + Long bindSocialUSer(Long sysUserId, String socialUSerId); | |
| 44 | + | |
| 45 | + /** | |
| 38 | 46 | * 修改用户 |
| 39 | 47 | * |
| 40 | 48 | * @param reqVO 用户信息 | ... | ... |
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImpl.java
| ... | ... | @@ -4,6 +4,8 @@ import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.io.IoUtil; |
| 5 | 5 | import cn.hutool.core.util.IdUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | +import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysUserSocialDO; | |
| 8 | +import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social.SysUserSocialMapper; | |
| 7 | 9 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 8 | 10 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 9 | 11 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| ... | ... | @@ -49,6 +51,8 @@ public class SysUserServiceImpl implements SysUserService { |
| 49 | 51 | |
| 50 | 52 | @Resource |
| 51 | 53 | private SysUserMapper userMapper; |
| 54 | + @Resource | |
| 55 | + private SysUserSocialMapper userSocialMapper; | |
| 52 | 56 | |
| 53 | 57 | @Resource |
| 54 | 58 | private SysDeptService deptService; |
| ... | ... | @@ -75,6 +79,15 @@ public class SysUserServiceImpl implements SysUserService { |
| 75 | 79 | } |
| 76 | 80 | |
| 77 | 81 | @Override |
| 82 | + public Long bindSocialUSer(Long sysUserId, String socialUSerId) { | |
| 83 | + SysUserSocialDO userSocialDO = new SysUserSocialDO(); | |
| 84 | + userSocialDO.setUserId(sysUserId); | |
| 85 | + userSocialDO.setSocialUserId(socialUSerId); | |
| 86 | + userSocialMapper.insert(userSocialDO); | |
| 87 | + return userSocialDO.getUserId(); | |
| 88 | + } | |
| 89 | + | |
| 90 | + @Override | |
| 78 | 91 | public void updateUser(SysUserUpdateReqVO reqVO) { |
| 79 | 92 | // 校验正确性 |
| 80 | 93 | this.checkCreateOrUpdate(reqVO.getId(), reqVO.getUsername(), reqVO.getMobile(), reqVO.getEmail(), | ... | ... |
yudao-admin-server/src/main/resources/application-local.yaml
| ... | ... | @@ -167,3 +167,221 @@ yudao: |
| 167 | 167 | - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 |
| 168 | 168 | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| 169 | 169 | demo: false # 关闭演示模式 |
| 170 | + | |
| 171 | + | |
| 172 | +# ums core | |
| 173 | +ums: | |
| 174 | + one-click-login: | |
| 175 | + # 一键登录是否开启, 默认 false | |
| 176 | + enable: true | |
| 177 | + # 一键登录请求处理 url, 默认 /authentication/one-click | |
| 178 | + login-processing-url: /authentication/one-click | |
| 179 | + # token 参数名称, 默认: accessToken | |
| 180 | + token-param-name: accessToken | |
| 181 | + # 其他请求参数名称列表(包括请求头名称), 此参数会传递到 OneClickLoginService.callback(String, Map) | |
| 182 | + # 与 UserDetailsRegisterService.registerUser(String, Map); 默认为: 空 | |
| 183 | + other-param-names: | |
| 184 | + - imei | |
| 185 | + # ================ 第三方授权登录相关配置 ================ | |
| 186 | + oauth: | |
| 187 | + # 第三方授权登录后如未注册用户是否支持自动注册功能, 默认: true | |
| 188 | + auto-sign-up: true | |
| 189 | + # 第三方授权登录后如未注册用户不支持自动注册功能, 则跳转到此 url 进行注册逻辑, 此 url 必须开发者自己实现; 默认: /signUp.html; | |
| 190 | + # 注意: 当 autoSignUp = false 时, 此属性才生效. | |
| 191 | + # 例如: 1. 设置值 "/signUp", 则跳转指定到 "/signUp" 进行注册. | |
| 192 | + # 2. 想返回自定义 json 数据到前端, 这里要设置 null , 在 Auth2LoginAuthenticationFilter 设置的 AuthenticationSuccessHandler | |
| 193 | + # 上处理返回 json; 判断是否为临时用户的条件是: Authentication.getPrincipal() 是否为 TemporaryUser 类型. | |
| 194 | + sign-up-url: null | |
| 195 | + # 用于第三方授权登录时, 未开启自动注册且用户是第一次授权登录的临时用户密码, 默认为: "". 注意: 生产环境更换密码 | |
| 196 | + temporary-user-password: "" | |
| 197 | + # 用于第三方授权登录时, 未开启自动注册且用户是第一次授权登录的临时用户的默认权限, 多个权限用逗号分开, 默认为: "ROLE_TEMPORARY_USER" | |
| 198 | + temporary-user-authorities: ROLE_TEMPORARY_USER | |
| 199 | + # 抑制反射警告, 支持 JDK11, 默认: false , 在确认 WARNING: An illegal reflective access operation has occurred 安全后, 可以打开此设置, 可以抑制反射警告. | |
| 200 | + suppress-reflect-warning: true | |
| 201 | + # 第三方服务商: providerId, 支持所有 JustAuth 支持的第三方授权登录, 目前有 32 家第三方授权登录 | |
| 202 | + github: | |
| 203 | + # 根据是否有设置 clientId 来动态加载相应 JustAuth 的 AuthXxxRequest | |
| 204 | + client-id: 4d4ee00e82f669f2ea8d | |
| 205 | + client-secret: 4050be113a83556b63bd991d606fded437b05235 | |
| 206 | + scopes: | |
| 207 | + # - 'repo:status' | |
| 208 | + # - 'public_repo' | |
| 209 | + # - 'repo:invite' | |
| 210 | + # - 'user:follow' | |
| 211 | + gitee: | |
| 212 | + client-id: eb5e3298cc10ead57cd40f9f36e7154ab2ea54dcb519684d7e10ca3fec5b1c1a | |
| 213 | + client-secret: 495b5542dc007e2d46d316c527bd05dac586f2ec31b96c551e164387b5edb1cb | |
| 214 | + scopes: | |
| 215 | + - user_info | |
| 216 | +# # 自定义 OAuth2 Login | |
| 217 | +# customize: | |
| 218 | +# client-id: c971cf1634460e18310a5d7cb0f55d7d143a72015b2f29aee6a0e8911efac7eb | |
| 219 | +# client-secret: 309c9521721e3eb385a99a6bde2755f3107c7e15f3b8e0527c9f3ea4d1ce33bb | |
| 220 | +# # 自定义第三方授权登录时, 当 Auth2Properties#customize 时有效, 此字段必须以驼峰方式命名. | |
| 221 | +# # 比如此字段的值为 umsCustomize, 那么 /auth2/authorization/customize 会替换为 /auth2/authorization/umsCustomize | |
| 222 | +# customize-provider-id: giteeCustomize | |
| 223 | +# # 自定义第三方授权登录, 当 Auth2Properties#customize 时有效, 设置第三方是否在国外, 默认: false. | |
| 224 | +# # 如果为 false 时, 设置 {@link HttpConfig} 的超时时间为 ums.oauth.proxy.timeout 的值. | |
| 225 | +# # 如果为 true 时, 设置 {@link HttpConfig} 的超时时间为 ums.oauth.proxy.foreignTimeout 的值. | |
| 226 | +# customize-is-foreign: false | |
| 227 | + # 第三方登录授权登录 url 前缀, 不包含 ServletContextPath,默认为 /auth2/authorization. | |
| 228 | + auth-login-url-prefix: /api/auth2/authorization | |
| 229 | + # 第三方登录回调处理 url 前缀 ,也就是 RedirectUrl 的前缀, 不包含 ServletContextPath,默认为 /auth2/login. | |
| 230 | + redirect-url-prefix: /api/auth2/login | |
| 231 | + # 第三方登录回调的域名, 例如:http://localhost:9090 默认为 "http://127.0.0.1", | |
| 232 | + # redirectUrl 直接由 {domain}/{servletContextPath}/{redirectUrlPrefix}/{providerId}(ums.oauth.[qq/gitee/weibo])组成 | |
| 233 | + domain: http://localhost:48080 | |
| 234 | + # 第三方授权登录成功后的默认权限, 多个权限用逗号分开, 默认为: "ROLE_USER" | |
| 235 | + default-authorities: ROLE_USER | |
| 236 | + # 是否支持内置的第三方登录用户表(user_connection) 和 auth_token 表. 默认: true. | |
| 237 | + # 注意: 如果为 false, 则必须重新实现 ConnectionService 接口. | |
| 238 | + enable-user-connection-and-auth-token-table: true | |
| 239 | + # 是否支持内置的第三方登录 token 表(auth_token). 默认: true. | |
| 240 | + enable-auth-token-table: true | |
| 241 | + | |
| 242 | + | |
| 243 | + # ================ start: 定时刷新 access token 定时任务相关配置 ================ | |
| 244 | + | |
| 245 | + # 是否支持定时刷新 AccessToken 定时任务. 默认: false. | |
| 246 | + # 支持分布式(分布式 IOC 容器中必须有 RedisConnectionFactory, 也就是说, 是否分布式执行依据 IOC 容器中是否有 RedisConnectionFactory) | |
| 247 | + enableRefreshTokenJob: false | |
| 248 | + # A cron-like expression. 0 * 2 * * ? 分别对应: second/minute/hour/day of month/month/day of week, | |
| 249 | + # 默认为: "0 * 2 * * ?", 凌晨 2 点启动定时任务, 支持分布式(分布式 IOC 容器中必须有 {@link RedisConnectionFactory}, | |
| 250 | + # 也就是说, 是否分布式执行依据 IOC 容器中是否有 {@link RedisConnectionFactory}) | |
| 251 | + refresh-token-job-cron: 0 0/5 * * * ? | |
| 252 | + # 定时刷新 accessToken 任务时, 批处理数据库的记录数. 注意: 分布式应用时, 此配置不同服务器配置必须是一样的. batchCount 大小需要根据实际生产环境进行优化 | |
| 253 | + batch-count: 1000 | |
| 254 | + # accessToken 的剩余有效期内进行刷新 accessToken, 默认: 24, 单位: 小时. 注意: 需要根据实际生产环境进行优化 | |
| 255 | + remaining-expire-in: 24 | |
| 256 | + | |
| 257 | + # ================ start: 定时刷新 access token 定时任务相关配置 ================ | |
| 258 | + | |
| 259 | + # JustAuth 内部参数设置 | |
| 260 | + just-auth: | |
| 261 | + # 忽略校验 state 参数,默认不开启。当 ignoreCheckState 为 true 时, me.zhyd.oauth.request.AuthDefaultRequest.login(AuthCallback) 将不会校验 state 的合法性。 | |
| 262 | + # 使用场景:当且仅当使用自实现 state 校验逻辑时开启 | |
| 263 | + # 以下场景使用方案仅作参考: | |
| 264 | + # 1. 授权、登录为同端,并且全部使用 JustAuth 实现时,该值建议设为 false; | |
| 265 | + # 2. 授权和登录为不同端实现时,比如前端页面拼装 authorizeUrl,并且前端自行对state进行校验,后端只负责使用code获取用户信息时,该值建议设为 true; | |
| 266 | + # 如非特殊需要,不建议开启这个配置 | |
| 267 | + # 该方案主要为了解决以下类似场景的问题:Since: 1.15.6, See Also: https://github.com/justauth/JustAuth/issues/83 | |
| 268 | + ignoreCheckState: false | |
| 269 | + # 默认 state 缓存过期时间:3分钟(PT180S) 鉴于授权过程中,根据个人的操作习惯,或者授权平台的不同(google等),每个授权流程的耗时也有差异, | |
| 270 | + # 不过单个授权流程一般不会太长 本缓存工具默认的过期时间设置为3分钟,即程序默认认为3分钟内的授权有效,超过3分钟则默认失效,失效后删除 | |
| 271 | + # 注意: 这是为了测试打断点时用的, 生产环境自己设置为合适数组或默认 | |
| 272 | + timeout: PT1800S | |
| 273 | + # JustAuth state 缓存类型, 默认 session | |
| 274 | + cacheType: session | |
| 275 | + # JustAuth state 缓存 key 前缀 | |
| 276 | + cacheKeyPrefix: 'JUST_AUTH:' | |
| 277 | + | |
| 278 | + # 用于 JustAuth 的代理(HttpClient)设置 | |
| 279 | + proxy: | |
| 280 | + # 是否支持代理, 默认为: false. | |
| 281 | + enable: false | |
| 282 | + # 针对国外服务可以单独设置代理类型, 默认 Proxy.Type.HTTP, enable = true 时生效. | |
| 283 | + proxy: HTTP | |
| 284 | + # 代理 host, enable = true 时生效. | |
| 285 | + hostname: | |
| 286 | + # 代理端口, enable = true 时生效. | |
| 287 | + port: | |
| 288 | + # 用于国内代理(HttpClient)超时, 默认 PT3S | |
| 289 | + timeout: PT3S | |
| 290 | + # 用于国外网站代理(HttpClient)超时, 默认 PT15S | |
| 291 | + foreign-timeout: PT150S | |
| 292 | + | |
| 293 | + | |
| 294 | + # =============== start: 第三方登录时用的数据库表 user_connection 与 auth_token 添加 redis cache =============== | |
| 295 | + cache: | |
| 296 | + # redisCacheManager 设置, 默认实现: 对查询结果 null 值进行缓存, 添加时更新缓存 null 值. | |
| 297 | + redis: | |
| 298 | + # 是否开启缓存, 默认 false | |
| 299 | + open: false | |
| 300 | + # 是否使用 spring IOC 容器中的 RedisConnectionFactory, 默认: false | |
| 301 | + # 如果使用 spring IOC 容器中的 RedisConnectionFactory,则要注意 cache.database-index 要与 spring.redis.database 一样。 | |
| 302 | + use-ioc-redis-connection-factory: true | |
| 303 | + cache: | |
| 304 | + # redis cache 存放的 database index, 默认: 0 | |
| 305 | + database-index: 1 | |
| 306 | + # 设置缓存管理器管理的缓存的默认过期时间, 默认: 200s | |
| 307 | + default-expire-time: PT200S | |
| 308 | + # cache ttl 。使用 0 声明一个永久的缓存。 默认: 180, 单位: 秒<br> | |
| 309 | + # 取缓存时间的 20% 作为动态的随机变量上下浮动, 防止同时缓存失效而缓存击穿 | |
| 310 | + entry-ttl: PT180S | |
| 311 | + # Names of the default caches to consider for caching operations defined in the annotated class. | |
| 312 | + # 此设置不对 user_connection 与 auth_token 使用的缓存名称(UCC/UCHC/UCHACC)产生影响. | |
| 313 | + cache-names: | |
| 314 | + - cacheName | |
| 315 | + # =============== end: 第三方登录时用的数据库表 user_connection 与 auth_token 添加 redis cache =============== | |
| 316 | + | |
| 317 | + # =============== start: 线程池配置 =============== | |
| 318 | + executor: | |
| 319 | + # 启动第三方授权登录用户的 accessToken 的定时任务时的 Executor 属性, 注意: 需要根据实际生产环境进行优化 | |
| 320 | + job-task-scheduled-executor: | |
| 321 | + # 线程池中空闲时保留的线程数, 默认: 0 | |
| 322 | + core-pool-size: 0 | |
| 323 | + # keep alive time, 默认: 10 | |
| 324 | + keep-alive-time: 0 | |
| 325 | + # keepAliveTime 时间单位, 默认: 毫秒 | |
| 326 | + time-unit: milliseconds | |
| 327 | + # 线程池名称, 默认: accessTokenJob | |
| 328 | + pool-name: accessTokenJob | |
| 329 | + # 拒绝策略, 默认: ABORT | |
| 330 | + rejected-execution-handler-policy: abort | |
| 331 | + # 线程池关闭过程的超时时间, 默认: PT10S | |
| 332 | + executor-shutdown-timeout: PT10S | |
| 333 | + # 更新第三方授权登录用户的 accessToken 的执行逻辑, 向本地数据库 auth_token 表获取过期或在一定时间内过期的 token 记录, | |
| 334 | + # 用 refreshToken 向第三方服务商更新 accessToken 信息的 Executor 属性, | |
| 335 | + # 注意: 定时刷新 accessToken 的执行逻辑是多线程的, 需要根据实际生产环境进行优化 | |
| 336 | + refresh-token: | |
| 337 | + # 程池中空闲时保留的线程数, 默认: 0 | |
| 338 | + core-pool-size: 0 | |
| 339 | + # 最大线程数, 默认: 本机核心数 | |
| 340 | + maximum-pool-size: 8 | |
| 341 | + # keep alive time, 默认: 5 | |
| 342 | + keep-alive-time: 5 | |
| 343 | + # keepAliveTime 时间单位, 默认: 秒 | |
| 344 | + time-unit: seconds | |
| 345 | + # blocking queue capacity, 默认: maximumPoolSize * 2 | |
| 346 | + blocking-queue-capacity: 16 | |
| 347 | + # 线程池名称, 默认: refreshToken | |
| 348 | + pool-name: refreshToken | |
| 349 | + # 拒绝策略, 默认: CALLER_RUNS 注意: 一般情况下不要更改默认设置, 没有实现 RefreshToken 逻辑被拒绝执行后的处理逻辑, 除非自己实现RefreshTokenJob.refreshTokenJob() 对 RefreshToken 逻辑被拒绝执行后的处理逻辑. | |
| 350 | + rejected-execution-handler-policy: caller_runs | |
| 351 | + # 线程池关闭过程的超时时间, 默认: 10 秒 | |
| 352 | + executor-shutdown-timeout: PT10S | |
| 353 | + # 第三方授权登录时, 异步更新用户的第三方授权用户信息与 token 信息的 Executor 属性, | |
| 354 | + # 注意: 第三方授权登录时是异步更新第三方用户信息与 token 信息到本地数据库时使用此配置, 需要根据实际生产环境进行优化 | |
| 355 | + user-connection-update: | |
| 356 | + # 程池中空闲时保留的线程数, 默认: 5 | |
| 357 | + core-pool-size: 5 | |
| 358 | + # 最大线程数, 默认: 本机核心数 | |
| 359 | + maximum-pool-size: 8 | |
| 360 | + # keep alive time, 默认: 10 | |
| 361 | + keep-alive-time: | |
| 362 | + # keepAliveTime 时间单位, 默认: 秒 | |
| 363 | + time-unit: seconds | |
| 364 | + # blocking queue capacity, 默认: maximumPoolSize * 2 | |
| 365 | + blocking-queue-capacity: 16 | |
| 366 | + # 线程池名称, 默认: updateConnection | |
| 367 | + pool-name: updateConnection | |
| 368 | + # 拒绝策略, 默认: CALLER_RUNS 注意: 一般情况下不要更改默认设置, 除非自己实现Auth2LoginAuthenticationProvider更新逻辑; | |
| 369 | + # 改成 ABORT 也支持, 默认实现 Auth2LoginAuthenticationProvider 是异步更新被拒绝执行后, 会执行同步更新. | |
| 370 | + rejected-execution-handler-policy: caller_runs | |
| 371 | + # 线程池关闭过程的超时时间, 默认: PT10S | |
| 372 | + executor-shutdown-timeout: PT10S | |
| 373 | + # =============== end: 线程池配置 =============== | |
| 374 | + | |
| 375 | + # =============== start: user_connection repository 配置 =============== | |
| 376 | + repository: | |
| 377 | + | |
| 378 | + # 第三方登录用户数据库表的字段 accessToken 与 refreshToken 加密专用密码 | |
| 379 | + text-encryptor-password: 7ca5d913a17b4942942d16a974e3fecc | |
| 380 | + # 第三方登录用户数据库表的字段 accessToken 与 refreshToken 加密专用 salt | |
| 381 | + text-encryptor-salt: cd538b1b077542aca5f86942b6507fe2 | |
| 382 | + # 是否在启动时检查并自动创建 userConnectionTableName 与 authTokenTableName, 默认: TRUE | |
| 383 | + enableStartUpInitializeTable: true | |
| 384 | + # 其他的有: 数据库表名称, 字段名称, curd sql 语句 等设置, 一般不需要更改, | |
| 385 | + # 如果要添加字段: 具体查看 RepositoryProperties 与 Auth2JdbcUsersConnectionRepository | |
| 386 | + # =============== end: user_connection repository 配置 =============== | |
| 387 | + | ... | ... |
yudao-admin-ui/src/api/login.js
| ... | ... | @@ -38,3 +38,19 @@ export function getCodeImg() { |
| 38 | 38 | method: 'get' |
| 39 | 39 | }) |
| 40 | 40 | } |
| 41 | + | |
| 42 | +// 接入第三方登录 | |
| 43 | +export function giteeLogin() { | |
| 44 | + return request({ | |
| 45 | + url: '/auth2/authorization/gitee', | |
| 46 | + method: 'get' | |
| 47 | + }) | |
| 48 | +} | |
| 49 | + | |
| 50 | +export function getToken(path) { | |
| 51 | + console.log({path}); | |
| 52 | + return request({ | |
| 53 | + url: '/auth2/login/gitee' + path, | |
| 54 | + method: 'get' | |
| 55 | + }) | |
| 56 | +} | ... | ... |
yudao-admin-ui/src/permission.js
| ... | ... | @@ -7,7 +7,7 @@ import { getToken } from '@/utils/auth' |
| 7 | 7 | |
| 8 | 8 | NProgress.configure({ showSpinner: false }) |
| 9 | 9 | |
| 10 | -const whiteList = ['/login', '/auth-redirect', '/bind', '/register'] | |
| 10 | +const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/oauthLogin/gitee'] | |
| 11 | 11 | |
| 12 | 12 | router.beforeEach((to, from, next) => { |
| 13 | 13 | NProgress.start() | ... | ... |
yudao-admin-ui/src/router/index.js
| ... | ... | @@ -44,6 +44,11 @@ export const constantRoutes = [ |
| 44 | 44 | hidden: true |
| 45 | 45 | }, |
| 46 | 46 | { |
| 47 | + path: '/oauthLogin/gitee', | |
| 48 | + component: (resolve) => require(['@/views/oauthLogin'], resolve), | |
| 49 | + hidden: true | |
| 50 | + }, | |
| 51 | + { | |
| 47 | 52 | path: '/404', |
| 48 | 53 | component: (resolve) => require(['@/views/error/404'], resolve), |
| 49 | 54 | hidden: true | ... | ... |
yudao-admin-ui/src/views/login.vue
| ... | ... | @@ -45,6 +45,11 @@ |
| 45 | 45 | <span v-else>登 录 中...</span> |
| 46 | 46 | </el-button> |
| 47 | 47 | </el-form-item> |
| 48 | + <el-form-item style="width:100%;"> | |
| 49 | + <el-button @click="doAuth2Login" title="使用 Gitee 帐号登录"> | |
| 50 | + <span class="gitee-login-title">使用 Gitee 帐号登录</span> | |
| 51 | + </el-button> | |
| 52 | + </el-form-item> | |
| 48 | 53 | </el-form> |
| 49 | 54 | <!-- 底部 --> |
| 50 | 55 | <div class="el-login-footer"> |
| ... | ... | @@ -54,7 +59,7 @@ |
| 54 | 59 | </template> |
| 55 | 60 | |
| 56 | 61 | <script> |
| 57 | -import { getCodeImg } from "@/api/login"; | |
| 62 | +import { getCodeImg,giteeLogin } from "@/api/login"; | |
| 58 | 63 | import Cookies from "js-cookie"; |
| 59 | 64 | import { encrypt, decrypt } from '@/utils/jsencrypt' |
| 60 | 65 | |
| ... | ... | @@ -135,6 +140,14 @@ export default { |
| 135 | 140 | }); |
| 136 | 141 | } |
| 137 | 142 | }); |
| 143 | + }, | |
| 144 | + doAuth2Login() { | |
| 145 | + console.log("开始Oauth登录..."); | |
| 146 | + this.loading = true; | |
| 147 | + giteeLogin().then((res) => { | |
| 148 | + console.log(res.url); | |
| 149 | + window.location.href = res.url; | |
| 150 | + }); | |
| 138 | 151 | } |
| 139 | 152 | } |
| 140 | 153 | }; | ... | ... |
yudao-admin-ui/src/views/oauthLogin.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div>登录成功!</div> | |
| 3 | +</template> | |
| 4 | + | |
| 5 | +<script> | |
| 6 | +import { getToken, setToken, removeToken } from '@/utils/auth' | |
| 7 | + | |
| 8 | +export default { | |
| 9 | + created() { | |
| 10 | + var token = this.getOauthToken(); | |
| 11 | + setToken(token); | |
| 12 | + this.$router.push({ path: this.redirect || "/" }).catch(() => {}); | |
| 13 | + }, | |
| 14 | + methods: { | |
| 15 | + getOauthToken() { | |
| 16 | + console.log(this); | |
| 17 | + var h = window.location.href; | |
| 18 | + var token = "token="; | |
| 19 | + return h.substring(h.indexOf(token)+token.length); | |
| 20 | + } | |
| 21 | + }, | |
| 22 | +} | |
| 23 | +</script> | |
| 24 | + | |
| 25 | +<style> | |
| 26 | + | |
| 27 | +</style> | ... | ... |
yudao-dependencies/pom.xml
| ... | ... | @@ -41,7 +41,7 @@ |
| 41 | 41 | <podam.version>7.2.6.RELEASE</podam.version> |
| 42 | 42 | <jedis-mock.version>0.1.16</jedis-mock.version> |
| 43 | 43 | <!-- 工具类相关 --> |
| 44 | - <lombok.version>1.18.12</lombok.version> | |
| 44 | + <lombok.version>1.18.20</lombok.version> | |
| 45 | 45 | <mapstruct.version>1.4.1.Final</mapstruct.version> |
| 46 | 46 | <hutool.version>5.6.1</hutool.version> |
| 47 | 47 | <easyexcel.verion>2.2.7</easyexcel.verion> | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/pom.xml
| ... | ... | @@ -39,6 +39,19 @@ |
| 39 | 39 | <artifactId>spring-boot-starter-security</artifactId> |
| 40 | 40 | </dependency> |
| 41 | 41 | |
| 42 | + <!-- justAuth 集成第三方登录 --> | |
| 43 | + <dependency> | |
| 44 | + <groupId>top.dcenter</groupId> | |
| 45 | + <artifactId>justAuth-spring-security-starter</artifactId> | |
| 46 | + <version>1.1.25</version> | |
| 47 | + <exclusions> | |
| 48 | + <exclusion> | |
| 49 | + <artifactId>slf4j-api</artifactId> | |
| 50 | + <groupId>org.slf4j</groupId> | |
| 51 | + </exclusion> | |
| 52 | + </exclusions> | |
| 53 | + </dependency> | |
| 54 | + | |
| 42 | 55 | </dependencies> |
| 43 | 56 | |
| 44 | 57 | </project> | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
| 1 | 1 | package cn.iocoder.yudao.framework.security.config; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.security.core.filter.JwtAuthenticationTokenFilter; |
| 4 | -import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; | |
| 5 | -import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; | |
| 6 | -import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; | |
| 4 | +import cn.iocoder.yudao.framework.security.core.handler.AbstractSignUpUrlAuthenticationSuccessHandler; | |
| 7 | 5 | import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; |
| 8 | 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 9 | -import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | |
| 7 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 10 | 8 | import org.springframework.beans.factory.annotation.Value; |
| 11 | 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| 12 | -import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 13 | 10 | import org.springframework.context.annotation.Bean; |
| 14 | 11 | import org.springframework.context.annotation.Configuration; |
| 15 | 12 | import org.springframework.http.HttpMethod; |
| ... | ... | @@ -19,13 +16,15 @@ import org.springframework.security.config.annotation.method.configuration.Enabl |
| 19 | 16 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
| 20 | 17 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
| 21 | 18 | import org.springframework.security.config.http.SessionCreationPolicy; |
| 22 | -import org.springframework.security.core.userdetails.UserDetailsService; | |
| 23 | -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |
| 24 | 19 | import org.springframework.security.crypto.password.PasswordEncoder; |
| 25 | 20 | import org.springframework.security.web.AuthenticationEntryPoint; |
| 26 | 21 | import org.springframework.security.web.access.AccessDeniedHandler; |
| 22 | +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; | |
| 27 | 23 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
| 28 | 24 | import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; |
| 25 | +import top.dcenter.ums.security.core.oauth.config.Auth2AutoConfigurer; | |
| 26 | +import top.dcenter.ums.security.core.oauth.properties.Auth2Properties; | |
| 27 | +import top.dcenter.ums.security.core.oauth.properties.OneClickLoginProperties; | |
| 29 | 28 | |
| 30 | 29 | import javax.annotation.Resource; |
| 31 | 30 | |
| ... | ... | @@ -48,7 +47,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap |
| 48 | 47 | * 自定义用户【认证】逻辑 |
| 49 | 48 | */ |
| 50 | 49 | @Resource |
| 51 | - private UserDetailsService userDetailsService; | |
| 50 | + private SecurityAuthFrameworkService userDetailsService; | |
| 52 | 51 | /** |
| 53 | 52 | * Spring Security 加密器 |
| 54 | 53 | */ |
| ... | ... | @@ -75,6 +74,15 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap |
| 75 | 74 | @Resource |
| 76 | 75 | private JwtAuthenticationTokenFilter authenticationTokenFilter; |
| 77 | 76 | |
| 77 | + @Autowired | |
| 78 | + private Auth2AutoConfigurer auth2AutoConfigurer; | |
| 79 | + @Autowired | |
| 80 | + private Auth2Properties auth2Properties; | |
| 81 | + @Autowired | |
| 82 | + private OneClickLoginProperties oneClickLoginProperties; | |
| 83 | + @Autowired | |
| 84 | + private AbstractSignUpUrlAuthenticationSuccessHandler authenticationSuccessHandler; | |
| 85 | + | |
| 78 | 86 | /** |
| 79 | 87 | * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 |
| 80 | 88 | * 通过覆写父类的该方法,添加 @Bean 注解,解决该问题 |
| ... | ... | @@ -114,7 +122,11 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap |
| 114 | 122 | */ |
| 115 | 123 | @Override |
| 116 | 124 | protected void configure(HttpSecurity httpSecurity) throws Exception { |
| 125 | + | |
| 117 | 126 | httpSecurity |
| 127 | + // ========= start: 使用 justAuth-spring-security-starter 必须步骤 ========= | |
| 128 | + // 添加 Auth2AutoConfigurer 使 OAuth2(justAuth) login 生效. | |
| 129 | + .apply(this.auth2AutoConfigurer).and() | |
| 118 | 130 | // 开启跨域 |
| 119 | 131 | .cors().and() |
| 120 | 132 | // CSRF 禁用,因为不使用 Session |
| ... | ... | @@ -123,39 +135,59 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap |
| 123 | 135 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() |
| 124 | 136 | // 一堆自定义的 Spring Security 处理器 |
| 125 | 137 | .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) |
| 126 | - .accessDeniedHandler(accessDeniedHandler).and() | |
| 138 | + .accessDeniedHandler(accessDeniedHandler).and() | |
| 139 | + .formLogin().loginPage(api("/login")).successHandler(authenticationSuccessHandler).and() | |
| 127 | 140 | // 设置每个请求的权限 |
| 128 | 141 | .authorizeRequests() |
| 129 | - // 登陆的接口,可匿名访问 | |
| 130 | - .antMatchers(api("/login")).anonymous() | |
| 131 | - // 通用的接口,可匿名访问 TODO 芋艿:需要抽象出去 | |
| 132 | - .antMatchers(api("/system/captcha/**")).anonymous() | |
| 133 | - // 静态资源,可匿名访问 | |
| 134 | - .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() | |
| 135 | - // 文件的获取接口,可匿名访问 | |
| 136 | - .antMatchers(api("/infra/file/get/**")).anonymous() | |
| 137 | - // Swagger 接口文档 | |
| 138 | - .antMatchers("/swagger-ui.html").anonymous() | |
| 139 | - .antMatchers("/swagger-resources/**").anonymous() | |
| 140 | - .antMatchers("/webjars/**").anonymous() | |
| 141 | - .antMatchers("/*/api-docs").anonymous() | |
| 142 | - // Spring Boot Admin Server 的安全配置 TODO 芋艿:需要抽象出去 | |
| 143 | - .antMatchers(adminSeverContextPath).anonymous() | |
| 144 | - .antMatchers(adminSeverContextPath + "/**").anonymous() | |
| 145 | - // Spring Boot Actuator 的安全配置 | |
| 146 | - .antMatchers("/actuator").anonymous() | |
| 147 | - .antMatchers("/actuator/**").anonymous() | |
| 148 | - // Druid 监控 TODO 芋艿:需要抽象出去 | |
| 149 | - .antMatchers("/druid/**").anonymous() | |
| 150 | - // 短信回调 API TODO 芋艿:需要抽象出去 | |
| 151 | - .antMatchers(api("/system/sms/callback/**")).anonymous() | |
| 152 | - // 除上面外的所有请求全部需要鉴权认证 | |
| 153 | - .anyRequest().authenticated() | |
| 142 | + // 登陆的接口,可匿名访问 | |
| 143 | + .antMatchers(api("/login")).anonymous() | |
| 144 | + // 通用的接口,可匿名访问 TODO 芋艿:需要抽象出去 | |
| 145 | + .antMatchers(api("/system/captcha/**")).anonymous() | |
| 146 | + // 静态资源,可匿名访问 | |
| 147 | + .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() | |
| 148 | + // 文件的获取接口,可匿名访问 | |
| 149 | + .antMatchers(api("/infra/file/get/**")).anonymous() | |
| 150 | + // Swagger 接口文档 | |
| 151 | + .antMatchers("/swagger-ui.html").anonymous() | |
| 152 | + .antMatchers("/favicon.ico").anonymous() | |
| 153 | + .antMatchers("/swagger-resources/**").anonymous() | |
| 154 | + .antMatchers("/webjars/**").anonymous() | |
| 155 | + .antMatchers("/*/api-docs").anonymous() | |
| 156 | + // Spring Boot Admin Server 的安全配置 TODO 芋艿:需要抽象出去 | |
| 157 | + .antMatchers(adminSeverContextPath).anonymous() | |
| 158 | + .antMatchers(adminSeverContextPath + "/**").anonymous() | |
| 159 | + // Spring Boot Actuator 的安全配置 | |
| 160 | + .antMatchers("/actuator").anonymous() | |
| 161 | + .antMatchers("/actuator/**").anonymous() | |
| 162 | + // Druid 监控 TODO 芋艿:需要抽象出去 | |
| 163 | + .antMatchers("/druid/**").anonymous() | |
| 164 | + // 短信回调 API TODO 芋艿:需要抽象出去 | |
| 165 | + .antMatchers(api("/system/sms/callback/**")).anonymous() | |
| 166 | + // oAuth2 auth2/login/gitee | |
| 167 | + .antMatchers(api("/auth2/login/**")).anonymous() | |
| 168 | + .antMatchers(api("/auth2/authorization/**")).anonymous() | |
| 169 | + .antMatchers("/api/callback/**").anonymous() | |
| 170 | + // 除上面外的所有请求全部需要鉴权认证 | |
| 171 | + .anyRequest().authenticated() | |
| 154 | 172 | .and() |
| 155 | 173 | .headers().frameOptions().disable(); |
| 156 | 174 | httpSecurity.logout().logoutUrl(api("/logout")).logoutSuccessHandler(logoutSuccessHandler); |
| 157 | 175 | // 添加 JWT Filter |
| 158 | 176 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
| 177 | + | |
| 178 | +// // 放行第三方登录入口地址与第三方登录回调地址 | |
| 179 | +// // @formatter:off | |
| 180 | +// httpSecurity.authorizeRequests() | |
| 181 | +// .antMatchers(HttpMethod.GET, | |
| 182 | +// auth2Properties.getRedirectUrlPrefix() + "/*", | |
| 183 | +// auth2Properties.getAuthLoginUrlPrefix() + "/*") | |
| 184 | +// .permitAll(); | |
| 185 | +// httpSecurity.authorizeRequests() | |
| 186 | +// .antMatchers(HttpMethod.POST, | |
| 187 | +// oneClickLoginProperties.getLoginProcessingUrl()) | |
| 188 | +// .permitAll(); | |
| 189 | +// // @formatter:on | |
| 190 | +// // ========= end: 使用 justAuth-spring-security-starter 必须步骤 ========= | |
| 159 | 191 | } |
| 160 | 192 | |
| 161 | 193 | private String api(String url) { | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/Auth2LoginUser.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.security.core; | |
| 2 | + | |
| 3 | +import com.alibaba.fastjson.JSONObject; | |
| 4 | +import lombok.AllArgsConstructor; | |
| 5 | +import lombok.Data; | |
| 6 | +import me.zhyd.oauth.enums.AuthUserGender; | |
| 7 | +import me.zhyd.oauth.model.AuthToken; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * @author weir | |
| 11 | + */ | |
| 12 | +@Data | |
| 13 | +public class Auth2LoginUser extends LoginUser { | |
| 14 | + /** | |
| 15 | + * 是否为临时注册的临时用户 | |
| 16 | + */ | |
| 17 | + private boolean isTempUser; | |
| 18 | + /** | |
| 19 | + * 用户第三方系统的唯一id。在调用方集成该组件时,可以用uuid + source唯一确定一个用户 | |
| 20 | + */ | |
| 21 | + private String thirdPartyUserId; | |
| 22 | + /** | |
| 23 | + * 用户昵称 | |
| 24 | + */ | |
| 25 | + private String nickname; | |
| 26 | + /** | |
| 27 | + * 用户头像 | |
| 28 | + */ | |
| 29 | + private String avatar; | |
| 30 | + /** | |
| 31 | + * 用户网址 | |
| 32 | + */ | |
| 33 | + private String blog; | |
| 34 | + /** | |
| 35 | + * 所在公司 | |
| 36 | + */ | |
| 37 | + private String company; | |
| 38 | + /** | |
| 39 | + * 位置 | |
| 40 | + */ | |
| 41 | + private String location; | |
| 42 | + /** | |
| 43 | + * 用户邮箱 | |
| 44 | + */ | |
| 45 | + private String email; | |
| 46 | + /** | |
| 47 | + * 用户备注(各平台中的用户个人介绍) | |
| 48 | + */ | |
| 49 | + private String remark; | |
| 50 | + /** | |
| 51 | + * 性别 | |
| 52 | + */ | |
| 53 | + private AuthUserGender gender; | |
| 54 | + /** | |
| 55 | + * 用户来源 | |
| 56 | + */ | |
| 57 | + private String source; | |
| 58 | + /** | |
| 59 | + * 用户授权的token信息 | |
| 60 | + */ | |
| 61 | + private AuthToken token; | |
| 62 | + /** | |
| 63 | + * 第三方平台返回的原始用户信息 | |
| 64 | + */ | |
| 65 | + private JSONObject rawUserInfo; | |
| 66 | +} | ... | ... |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/AbstractSignUpUrlAuthenticationSuccessHandler.java
0 → 100644
| 1 | +/* | |
| 2 | + * MIT License | |
| 3 | + * Copyright (c) 2020-2029 YongWu zheng (dcenter.top and gitee.com/pcore and github.com/ZeroOrInfinity) | |
| 4 | + * | |
| 5 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 6 | + * of this software and associated documentation files (the "Software"), to deal | |
| 7 | + * in the Software without restriction, including without limitation the rights | |
| 8 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 9 | + * copies of the Software, and to permit persons to whom the Software is | |
| 10 | + * furnished to do so, subject to the following conditions: | |
| 11 | + * | |
| 12 | + * The above copyright notice and this permission notice shall be included in all | |
| 13 | + * copies or substantial portions of the Software. | |
| 14 | + * | |
| 15 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 16 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 17 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 18 | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 19 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| 21 | + * SOFTWARE. | |
| 22 | + */ | |
| 23 | +package cn.iocoder.yudao.framework.security.core.handler; | |
| 24 | + | |
| 25 | +import cn.iocoder.yudao.framework.security.core.LoginUser; | |
| 26 | +import org.springframework.http.HttpStatus; | |
| 27 | +import org.springframework.security.core.Authentication; | |
| 28 | +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; | |
| 29 | +import org.springframework.security.web.savedrequest.HttpSessionRequestCache; | |
| 30 | +import org.springframework.security.web.savedrequest.RequestCache; | |
| 31 | +import org.springframework.security.web.savedrequest.SavedRequest; | |
| 32 | +import top.dcenter.ums.security.core.oauth.userdetails.TemporaryUser; | |
| 33 | +import top.dcenter.ums.security.core.vo.ResponseResult; | |
| 34 | + | |
| 35 | +import javax.servlet.ServletException; | |
| 36 | +import javax.servlet.http.HttpServletRequest; | |
| 37 | +import javax.servlet.http.HttpServletResponse; | |
| 38 | +import java.io.IOException; | |
| 39 | + | |
| 40 | +import static org.springframework.util.StringUtils.hasText; | |
| 41 | +import static top.dcenter.ums.security.core.oauth.util.MvcUtil.*; | |
| 42 | + | |
| 43 | +/** | |
| 44 | + * @author weir | |
| 45 | + */ | |
| 46 | +public class AbstractSignUpUrlAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { | |
| 47 | + private RequestCache requestCache = new HttpSessionRequestCache(); | |
| 48 | + | |
| 49 | + @Override | |
| 50 | + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { | |
| 51 | + if (requestCache.getRequest(request, response) != null) { | |
| 52 | + requestCache.getRequest(request, response); | |
| 53 | + } | |
| 54 | + super.onAuthenticationSuccess(request,response,authentication); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @Override | |
| 58 | + public void setRequestCache(RequestCache requestCache) { | |
| 59 | + this.requestCache = requestCache; | |
| 60 | + } | |
| 61 | +} | ... | ... |