Commit 133cb49c42cd22ce71ddf51bb8288fc69558a8fe

Authored by weir
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
  1 +package cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user;
  2 +
  3 +/**
  4 + * @author weir
  5 + */
  6 +public class AuthUser {
  7 +}
... ...
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
... ... @@ -11,10 +11,12 @@ import lombok.Getter;
11 11 @Getter
12 12 @AllArgsConstructor
13 13 public enum SysSexEnum {
14   -
15   - MALE(1), // 男
16   - FEMALE(2); // 女
17   -
  14 + /** 男 */
  15 + MALE(1),
  16 + /** 女 */
  17 + FEMALE(2),
  18 + /* 未知 */
  19 + UNKNOWN(3);
18 20 /**
19 21 * 性别
20 22 */
... ...
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 &#39;@/utils/auth&#39;
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 +}
... ...