Commit 6a61db85082d36ffd5d3a26cc0937de11a5b41c1

Authored by YunaiV
1 parent 41c324ac

security: query string 支持传递 token 参数,解决 ws token 认证

yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java
... ... @@ -19,6 +19,13 @@ public class SecurityProperties {
19 19 */
20 20 @NotEmpty(message = "Token Header 不能为空")
21 21 private String tokenHeader = "Authorization";
  22 + /**
  23 + * HTTP 请求时,访问令牌的请求参数
  24 + *
  25 + * 初始目的:解决 WebSocket 无法通过 header 传参,只能通过 token 参数拼接
  26 + */
  27 + @NotEmpty(message = "Token Parameter 不能为空")
  28 + private String tokenParameter = "token";
22 29  
23 30 /**
24 31 * mock 模式的开关
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
... ... @@ -129,8 +129,6 @@ public class YudaoWebSecurityConfigurerAdapter {
129 129 .antMatchers(buildAppApi("/**")).permitAll()
130 130 // 1.5 验证码captcha 允许匿名访问
131 131 .antMatchers("/captcha/get", "/captcha/check").permitAll()
132   - // 1.6 webSocket 允许匿名访问
133   - .antMatchers("/websocket/message").permitAll()
134 132 // ②:每个项目的自定义规则
135 133 .and().authorizeRequests(registry -> // 下面,循环设置自定义规则
136 134 authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(registry)))
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
... ... @@ -41,7 +41,8 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
41 41 @SuppressWarnings("NullableProblems")
42 42 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
43 43 throws ServletException, IOException {
44   - String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
  44 + String token = SecurityFrameworkUtils.obtainAuthorization(request,
  45 + securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
45 46 if (StrUtil.isNotEmpty(token)) {
46 47 Integer userType = WebFrameworkUtils.getLoginUserType(request);
47 48 try {
... ... @@ -74,7 +75,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
74 75 return null;
75 76 }
76 77 // 用户类型不匹配,无权限
77   - if (ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
  78 + // 注意:只有 /admin-api/* 和 /app-api/* 有 userType,才需要比对用户类型
  79 + // TODO 芋艿:ws 要不要区分开?
  80 + if (userType != null
  81 + && ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
78 82 throw new AccessDeniedException("错误的用户类型");
79 83 }
80 84 // 构建登录用户
... ...
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
1 1 package cn.iocoder.yudao.framework.security.core.util;
2 2  
  3 +import cn.hutool.core.util.StrUtil;
3 4 import cn.iocoder.yudao.framework.security.core.LoginUser;
4 5 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
5 6 import org.springframework.lang.Nullable;
... ... @@ -20,6 +21,9 @@ import java.util.Collections;
20 21 */
21 22 public class SecurityFrameworkUtils {
22 23  
  24 + /**
  25 + * HEADER 认证头 value 的前缀
  26 + */
23 27 public static final String AUTHORIZATION_BEARER = "Bearer";
24 28  
25 29 private SecurityFrameworkUtils() {}
... ... @@ -28,19 +32,23 @@ public class SecurityFrameworkUtils {
28 32 * 从请求中,获得认证 Token
29 33 *
30 34 * @param request 请求
31   - * @param header 认证 Token 对应的 Header 名字
  35 + * @param headerName 认证 Token 对应的 Header 名字
  36 + * @param parameterName 认证 Token 对应的 Parameter 名字
32 37 * @return 认证 Token
33 38 */
34   - public static String obtainAuthorization(HttpServletRequest request, String header) {
35   - String authorization = request.getHeader(header);
36   - if (!StringUtils.hasText(authorization)) {
37   - return null;
  39 + public static String obtainAuthorization(HttpServletRequest request,
  40 + String headerName, String parameterName) {
  41 + // 1. 获得 Token。优先级:Header > Parameter
  42 + String token = request.getHeader(headerName);
  43 + if (StrUtil.isEmpty(token)) {
  44 + token = request.getParameter(parameterName);
38 45 }
39   - int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
40   - if (index == -1) { // 未找到
  46 + if (!StringUtils.hasText(token)) {
41 47 return null;
42 48 }
43   - return authorization.substring(index + 7).trim();
  49 + // 2. 去除 Token 中带的 Bearer
  50 + int index = token.indexOf(AUTHORIZATION_BEARER + " ");
  51 + return index >= 0 ? token.substring(index + 7).trim() : token;
44 52 }
45 53  
46 54 /**
... ...
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
... ... @@ -53,7 +53,8 @@ public class AppAuthController {
53 53 @PermitAll
54 54 @Operation(summary = "登出系统")
55 55 public CommonResult<Boolean> logout(HttpServletRequest request) {
56   - String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
  56 + String token = SecurityFrameworkUtils.obtainAuthorization(request,
  57 + securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
57 58 if (StrUtil.isNotBlank(token)) {
58 59 authService.logout(token);
59 60 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
... ... @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 7 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
8 8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
9 9 import cn.iocoder.yudao.framework.security.config.SecurityProperties;
  10 +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
10 11 import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*;
11 12 import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
12 13 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
... ... @@ -38,7 +39,6 @@ import java.util.Set;
38 39 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
39 40 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
40 41 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
41   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization;
42 42  
43 43 @Tag(name = "管理后台 - 认证")
44 44 @RestController
... ... @@ -76,7 +76,8 @@ public class AuthController {
76 76 @Operation(summary = "登出系统")
77 77 @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
78 78 public CommonResult<Boolean> logout(HttpServletRequest request) {
79   - String token = obtainAuthorization(request, securityProperties.getTokenHeader());
  79 + String token = SecurityFrameworkUtils.obtainAuthorization(request,
  80 + securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
80 81 if (StrUtil.isNotBlank(token)) {
81 82 authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
82 83 }
... ...