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 package cn.iocoder.yudao.framework.tenant.core.web; 1 package cn.iocoder.yudao.framework.tenant.core.web;
2 2
3 -import cn.hutool.core.util.StrUtil;  
4 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; 3 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  4 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
5 import org.springframework.web.filter.OncePerRequestFilter; 5 import org.springframework.web.filter.OncePerRequestFilter;
6 6
7 import javax.servlet.FilterChain; 7 import javax.servlet.FilterChain;
@@ -24,9 +24,9 @@ public class TenantContextWebFilter extends OncePerRequestFilter { @@ -24,9 +24,9 @@ public class TenantContextWebFilter extends OncePerRequestFilter {
24 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) 24 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
25 throws ServletException, IOException { 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 try { 31 try {
32 chain.doFilter(request, response); 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,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 * 基于 token 退出登录 109 * 基于 token 退出登录
121 * 110 *
122 * @param request 请求 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,6 +8,7 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
8 import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; 8 import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
9 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 9 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
10 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 10 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
  11 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
11 import lombok.RequiredArgsConstructor; 12 import lombok.RequiredArgsConstructor;
12 import org.springframework.web.filter.OncePerRequestFilter; 13 import org.springframework.web.filter.OncePerRequestFilter;
13 14
@@ -38,12 +39,13 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @@ -38,12 +39,13 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
38 throws ServletException, IOException { 39 throws ServletException, IOException {
39 String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); 40 String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
40 if (StrUtil.isNotEmpty(token)) { 41 if (StrUtil.isNotEmpty(token)) {
  42 + Integer userType = WebFrameworkUtils.getLoginUserType(request);
41 try { 43 try {
42 // 验证 token 有效性 44 // 验证 token 有效性
43 LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token); 45 LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token);
44 // 模拟 Login 功能,方便日常开发调试 46 // 模拟 Login 功能,方便日常开发调试
45 if (loginUser == null) { 47 if (loginUser == null) {
46 - loginUser = mockLoginUser(request, token); 48 + loginUser = mockLoginUser(request, token, userType);
47 } 49 }
48 // 设置当前用户 50 // 设置当前用户
49 if (loginUser != null) { 51 if (loginUser != null) {
@@ -67,9 +69,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @@ -67,9 +69,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
67 * 69 *
68 * @param request 请求 70 * @param request 请求
69 * @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号 71 * @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号
  72 + * @param userType 用户类型
70 * @return 模拟的 LoginUser 73 * @return 模拟的 LoginUser
71 */ 74 */
72 - private LoginUser mockLoginUser(HttpServletRequest request, String token) { 75 + private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) {
73 if (!securityProperties.getMockEnable()) { 76 if (!securityProperties.getMockEnable()) {
74 return null; 77 return null;
75 } 78 }
@@ -77,8 +80,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { @@ -77,8 +80,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
77 if (!token.startsWith(securityProperties.getMockSecret())) { 80 if (!token.startsWith(securityProperties.getMockSecret())) {
78 return null; 81 return null;
79 } 82 }
  83 + // 构建模拟用户
80 Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); 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,14 +21,6 @@ public interface SecurityAuthFrameworkService extends UserDetailsService {
21 LoginUser verifyTokenAndRefresh(String token); 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 * 基于 token 退出登录 24 * 基于 token 退出登录
33 * 25 *
34 * @param token token 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,6 +7,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
7 import cn.iocoder.yudao.framework.web.core.filter.XssFilter; 7 import cn.iocoder.yudao.framework.web.core.filter.XssFilter;
8 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; 8 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
9 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; 9 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
  10 +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
10 import org.springframework.beans.factory.annotation.Value; 11 import org.springframework.beans.factory.annotation.Value;
11 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 12 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
12 import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -65,6 +66,13 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer { @@ -65,6 +66,13 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
65 return new GlobalResponseBodyHandler(); 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 // ========== Filter 相关 ========== 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 package cn.iocoder.yudao.framework.web.core.util; 1 package cn.iocoder.yudao.framework.web.core.util;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  6 +import cn.iocoder.yudao.framework.web.config.WebProperties;
5 import org.springframework.web.context.request.RequestAttributes; 7 import org.springframework.web.context.request.RequestAttributes;
6 import org.springframework.web.context.request.RequestContextHolder; 8 import org.springframework.web.context.request.RequestContextHolder;
7 import org.springframework.web.context.request.ServletRequestAttributes; 9 import org.springframework.web.context.request.ServletRequestAttributes;
@@ -21,16 +23,43 @@ public class WebFrameworkUtils { @@ -21,16 +23,43 @@ public class WebFrameworkUtils {
21 23
22 private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; 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 public static void setLoginUserId(ServletRequest request, Long userId) { 46 public static void setLoginUserId(ServletRequest request, Long userId) {
25 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); 47 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId);
26 } 48 }
27 49
  50 + /**
  51 + * 设置用户类型
  52 + *
  53 + * @param request 请求
  54 + * @param userType 用户类型
  55 + */
28 public static void setLoginUserType(ServletRequest request, Integer userType) { 56 public static void setLoginUserType(ServletRequest request, Integer userType) {
29 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType); 57 request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType);
30 } 58 }
31 59
32 /** 60 /**
33 * 获得当前用户的编号,从请求中 61 * 获得当前用户的编号,从请求中
  62 + * 注意:该方法仅限于 framework 框架使用!!!
34 * 63 *
35 * @param request 请求 64 * @param request 请求
36 * @return 用户编号 65 * @return 用户编号
@@ -43,7 +72,8 @@ public class WebFrameworkUtils { @@ -43,7 +72,8 @@ public class WebFrameworkUtils {
43 } 72 }
44 73
45 /** 74 /**
46 - * 获得当前用户的类型,从请求中 75 + * 获得当前用户的类型
  76 + * 注意:该方法仅限于 web 相关的 framework 组件使用!!!
47 * 77 *
48 * @param request 请求 78 * @param request 请求
49 * @return 用户编号 79 * @return 用户编号
@@ -52,7 +82,19 @@ public class WebFrameworkUtils { @@ -52,7 +82,19 @@ public class WebFrameworkUtils {
52 if (request == null) { 82 if (request == null) {
53 return null; 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 public static Integer getLoginUserType() { 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,21 +212,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
212 } 212 }
213 213
214 @Override 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 public void logout(String token) { 215 public void logout(String token) {
231 // 查询用户信息 216 // 查询用户信息
232 LoginUser loginUser = userSessionApi.getLoginUser(token); 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,7 +12,6 @@ public enum LoginLogTypeEnum {
12 12
13 LOGIN_USERNAME(100), // 使用账号登录 13 LOGIN_USERNAME(100), // 使用账号登录
14 LOGIN_SOCIAL(101), // 使用社交登录 14 LOGIN_SOCIAL(101), // 使用社交登录
15 - LOGIN_MOCK(102), // 使用 Mock 登录  
16 LOGIN_MOBILE(103), // 使用手机登陆 15 LOGIN_MOBILE(103), // 使用手机登陆
17 LOGIN_SMS(104), // 使用短信登陆 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 ### 请求 /login 接口 => 成功 1 ### 请求 /login 接口 => 成功
2 -POST {{baseUrl}}/system/login 2 +POST {{baseUrl}}/system/auth/login
3 Content-Type: application/json 3 Content-Type: application/json
4 tenant-id: {{adminTenentId}} 4 tenant-id: {{adminTenentId}}
5 5
@@ -11,7 +11,7 @@ tenant-id: {{adminTenentId}} @@ -11,7 +11,7 @@ tenant-id: {{adminTenentId}}
11 } 11 }
12 12
13 ### 请求 /login 接口 => 成功(无验证码) 13 ### 请求 /login 接口 => 成功(无验证码)
14 -POST {{baseUrl}}/system/login 14 +POST {{baseUrl}}/system/auth/login
15 Content-Type: application/json 15 Content-Type: application/json
16 tenant-id: {{adminTenentId}} 16 tenant-id: {{adminTenentId}}
17 17
@@ -21,7 +21,7 @@ tenant-id: {{adminTenentId}} @@ -21,7 +21,7 @@ tenant-id: {{adminTenentId}}
21 } 21 }
22 22
23 ### 请求 /get-permission-info 接口 => 成功 23 ### 请求 /get-permission-info 接口 => 成功
24 -GET {{baseUrl}}/system/get-permission-info 24 +GET {{baseUrl}}/system/auth/get-permission-info
25 Authorization: Bearer {{token}} 25 Authorization: Bearer {{token}}
26 tenant-id: {{adminTenentId}} 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,19 +83,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
83 } 83 }
84 84
85 @Override 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 public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) { 86 public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
100 // 判断验证码是否正确 87 // 判断验证码是否正确
101 verifyCaptcha(reqVO); 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,32 +97,6 @@ public class AuthServiceImplTest extends BaseDbUnitTest {
97 } 97 }
98 98
99 @Test 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 public void testLogin_captchaNotFound() { 100 public void testLogin_captchaNotFound() {
127 // 准备参数 101 // 准备参数
128 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); 102 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class);