Commit baadb5a9370d2e57b7ea3b7247c0ab34e387e31c

Authored by YunaiV
1 parent 73bf0b6f

简化 mock login 模拟登录的实现,由 TokenAuthenticationFilter 直接实现

Showing 11 changed files with 67 additions and 86 deletions
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantContextWebFilter.java
1 1 package cn.iocoder.yudao.framework.tenant.core.web;
2 2  
3   -import cn.hutool.core.util.StrUtil;
4 3 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  4 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
5 5 import org.springframework.web.filter.OncePerRequestFilter;
6 6  
7 7 import javax.servlet.FilterChain;
... ... @@ -24,9 +24,9 @@ public class TenantContextWebFilter extends OncePerRequestFilter {
24 24 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
25 25 throws ServletException, IOException {
26 26 // 设置
27   - String tenantId = request.getHeader(HEADER_TENANT_ID);
28   - if (StrUtil.isNotEmpty(tenantId)) {
29   - TenantContextHolder.setTenantId(Long.valueOf(tenantId));
  27 + Long tenantId = WebFrameworkUtils.getTenantId(request);
  28 + if (tenantId != null) {
  29 + TenantContextHolder.setTenantId(tenantId);
30 30 }
31 31 try {
32 32 chain.doFilter(request, response);
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java
... ... @@ -106,17 +106,6 @@ public class MultiUserDetailsAuthenticationProvider extends AbstractUserDetailsA
106 106 }
107 107  
108 108 /**
109   - * 模拟指定用户编号的 LoginUser
110   - *
111   - * @param request 请求
112   - * @param userId 用户编号
113   - * @return 登录用户
114   - */
115   - public LoginUser mockLogin(HttpServletRequest request, Long userId) {
116   - return selectService(request).mockLogin(userId);
117   - }
118   -
119   - /**
120 109 * 基于 token 退出登录
121 110 *
122 111 * @param request 请求
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
... ... @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
8 8 import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
9 9 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
10 10 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
  11 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
11 12 import lombok.RequiredArgsConstructor;
12 13 import org.springframework.web.filter.OncePerRequestFilter;
13 14  
... ... @@ -38,12 +39,13 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
38 39 throws ServletException, IOException {
39 40 String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
40 41 if (StrUtil.isNotEmpty(token)) {
  42 + Integer userType = WebFrameworkUtils.getLoginUserType(request);
41 43 try {
42 44 // 验证 token 有效性
43 45 LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token);
44 46 // 模拟 Login 功能,方便日常开发调试
45 47 if (loginUser == null) {
46   - loginUser = mockLoginUser(request, token);
  48 + loginUser = mockLoginUser(request, token, userType);
47 49 }
48 50 // 设置当前用户
49 51 if (loginUser != null) {
... ... @@ -67,9 +69,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
67 69 *
68 70 * @param request 请求
69 71 * @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号
  72 + * @param userType 用户类型
70 73 * @return 模拟的 LoginUser
71 74 */
72   - private LoginUser mockLoginUser(HttpServletRequest request, String token) {
  75 + private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) {
73 76 if (!securityProperties.getMockEnable()) {
74 77 return null;
75 78 }
... ... @@ -77,8 +80,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
77 80 if (!token.startsWith(securityProperties.getMockSecret())) {
78 81 return null;
79 82 }
  83 + // 构建模拟用户
80 84 Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
81   - return authenticationProvider.mockLogin(request, userId);
  85 + return new LoginUser().setId(userId).setUserType(userType)
  86 + .setTenantId(WebFrameworkUtils.getTenantId(request));
82 87 }
83 88  
84 89 }
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java
... ... @@ -21,14 +21,6 @@ public interface SecurityAuthFrameworkService extends UserDetailsService {
21 21 LoginUser verifyTokenAndRefresh(String token);
22 22  
23 23 /**
24   - * 模拟指定用户编号的 LoginUser
25   - *
26   - * @param userId 用户编号
27   - * @return 登录用户
28   - */
29   - LoginUser mockLogin(Long userId);
30   -
31   - /**
32 24 * 基于 token 退出登录
33 25 *
34 26 * @param token token
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
7 7 import cn.iocoder.yudao.framework.web.core.filter.XssFilter;
8 8 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
9 9 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
  10 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
10 11 import org.springframework.beans.factory.annotation.Value;
11 12 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
12 13 import org.springframework.boot.context.properties.EnableConfigurationProperties;
... ... @@ -65,6 +66,13 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
65 66 return new GlobalResponseBodyHandler();
66 67 }
67 68  
  69 + @Bean
  70 + @SuppressWarnings("InstantiationOfUtilityClass")
  71 + public WebFrameworkUtils webFrameworkUtils(WebProperties webProperties) {
  72 + // 由于 WebFrameworkUtils 需要使用到 webProperties 属性,所以注册为一个 Bean
  73 + return new WebFrameworkUtils(webProperties);
  74 + }
  75 +
68 76 // ========== Filter 相关 ==========
69 77  
70 78 /**
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
1 1 package cn.iocoder.yudao.framework.web.core.util;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.web.config.WebProperties;
5 7 import org.springframework.web.context.request.RequestAttributes;
6 8 import org.springframework.web.context.request.RequestContextHolder;
7 9 import org.springframework.web.context.request.ServletRequestAttributes;
... ... @@ -21,16 +23,43 @@ public class WebFrameworkUtils {
21 23  
22 24 private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result";
23 25  
  26 + private static final String HEADER_TENANT_ID = "tenant-id";
  27 +
  28 + private static WebProperties properties;
  29 +
  30 + public WebFrameworkUtils(WebProperties webProperties) {
  31 + WebFrameworkUtils.properties = webProperties;
  32 + }
  33 +
  34 + /**
  35 + * 获得租户编号,从 header 中
  36 + * 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供
  37 + *
  38 + * @param request 请求
  39 + * @return 租户编号
  40 + */
  41 + public static Long getTenantId(HttpServletRequest request) {
  42 + String tenantId = request.getHeader(HEADER_TENANT_ID);
  43 + return StrUtil.isNotEmpty(tenantId) ? Long.valueOf(tenantId) : null;
  44 + }
  45 +
24 46 public static void setLoginUserId(ServletRequest request, Long userId) {
25 47 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId);
26 48 }
27 49  
  50 + /**
  51 + * 设置用户类型
  52 + *
  53 + * @param request 请求
  54 + * @param userType 用户类型
  55 + */
28 56 public static void setLoginUserType(ServletRequest request, Integer userType) {
29 57 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType);
30 58 }
31 59  
32 60 /**
33 61 * 获得当前用户的编号,从请求中
  62 + * 注意:该方法仅限于 framework 框架使用!!!
34 63 *
35 64 * @param request 请求
36 65 * @return 用户编号
... ... @@ -43,7 +72,8 @@ public class WebFrameworkUtils {
43 72 }
44 73  
45 74 /**
46   - * 获得当前用户的类型,从请求中
  75 + * 获得当前用户的类型
  76 + * 注意:该方法仅限于 web 相关的 framework 组件使用!!!
47 77 *
48 78 * @param request 请求
49 79 * @return 用户编号
... ... @@ -52,7 +82,19 @@ public class WebFrameworkUtils {
52 82 if (request == null) {
53 83 return null;
54 84 }
55   - return (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE);
  85 + // 1. 优先,从 Attribute 中获取
  86 + Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE);
  87 + if (userType == null) {
  88 + return null;
  89 + }
  90 + // 2. 其次,基于 URL 前缀的约定
  91 + if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
  92 + return UserTypeEnum.ADMIN.getValue();
  93 + }
  94 + if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
  95 + return UserTypeEnum.MEMBER.getValue();
  96 + }
  97 + return null;
56 98 }
57 99  
58 100 public static Integer getLoginUserType() {
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
... ... @@ -212,21 +212,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
212 212 }
213 213  
214 214 @Override
215   - public LoginUser mockLogin(Long userId) {
216   - // 获取用户编号对应的 UserDO
217   - MemberUserDO user = userService.getUser(userId);
218   - if (user == null) {
219   - throw new UsernameNotFoundException(String.valueOf(userId));
220   - }
221   -
222   - // 执行登陆
223   - createLoginLog(userId, user.getMobile(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS);
224   -
225   - // 创建 LoginUser 对象
226   - return buildLoginUser(user);
227   - }
228   -
229   - @Override
230 215 public void logout(String token) {
231 216 // 查询用户信息
232 217 LoginUser loginUser = userSessionApi.getLoginUser(token);
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java
... ... @@ -12,7 +12,6 @@ public enum LoginLogTypeEnum {
12 12  
13 13 LOGIN_USERNAME(100), // 使用账号登录
14 14 LOGIN_SOCIAL(101), // 使用社交登录
15   - LOGIN_MOCK(102), // 使用 Mock 登录
16 15 LOGIN_MOBILE(103), // 使用手机登陆
17 16 LOGIN_SMS(104), // 使用短信登陆
18 17  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http
1 1 ### 请求 /login 接口 => 成功
2   -POST {{baseUrl}}/system/login
  2 +POST {{baseUrl}}/system/auth/login
3 3 Content-Type: application/json
4 4 tenant-id: {{adminTenentId}}
5 5  
... ... @@ -11,7 +11,7 @@ tenant-id: {{adminTenentId}}
11 11 }
12 12  
13 13 ### 请求 /login 接口 => 成功(无验证码)
14   -POST {{baseUrl}}/system/login
  14 +POST {{baseUrl}}/system/auth/login
15 15 Content-Type: application/json
16 16 tenant-id: {{adminTenentId}}
17 17  
... ... @@ -21,7 +21,7 @@ tenant-id: {{adminTenentId}}
21 21 }
22 22  
23 23 ### 请求 /get-permission-info 接口 => 成功
24   -GET {{baseUrl}}/system/get-permission-info
  24 +GET {{baseUrl}}/system/auth/get-permission-info
25 25 Authorization: Bearer {{token}}
26 26 tenant-id: {{adminTenentId}}
27 27  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
... ... @@ -83,19 +83,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
83 83 }
84 84  
85 85 @Override
86   - public LoginUser mockLogin(Long userId) {
87   - // 获取用户编号对应的 AdminUserDO
88   - AdminUserDO user = userService.getUser(userId);
89   - if (user == null) {
90   - throw new UsernameNotFoundException(String.valueOf(userId));
91   - }
92   - createLoginLog(userId, user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS);
93   -
94   - // 创建 LoginUser 对象
95   - return buildLoginUser(user);
96   - }
97   -
98   - @Override
99 86 public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
100 87 // 判断验证码是否正确
101 88 verifyCaptcha(reqVO);
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
... ... @@ -97,32 +97,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
97 97 }
98 98  
99 99 @Test
100   - public void testMockLogin_success() {
101   - // 准备参数
102   - Long userId = randomLongId();
103   - // mock 方法 01
104   - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId));
105   - when(userService.getUser(eq(userId))).thenReturn(user);
106   -
107   - // 调用
108   - LoginUser loginUser = authService.mockLogin(userId);
109   - // 断言
110   - AssertUtils.assertPojoEquals(user, loginUser, "updateTime");
111   - }
112   -
113   - @Test
114   - public void testMockLogin_userNotFound() {
115   - // 准备参数
116   - Long userId = randomLongId();
117   - // mock 方法
118   -
119   - // 调用, 并断言异常
120   - assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常
121   - () -> authService.mockLogin(userId),
122   - String.valueOf(userId)); // 异常提示为 userId
123   - }
124   -
125   - @Test
126 100 public void testLogin_captchaNotFound() {
127 101 // 准备参数
128 102 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);
... ...