Browse Source

pms 钉钉H5微应用免登录接口

zhangcl 5 ngày trước cách đây
mục cha
commit
f3da16543d

+ 109 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java

@@ -8,6 +8,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 /**
@@ -77,4 +78,112 @@ public class StrUtils {
                 .collect(Collectors.joining("\n"));
     }
 
+    /**
+     *
+     * @param value
+     * @return
+     */
+    public static String stripXSS(String value) {
+        if (value != null) {
+            value = value.replaceAll("", "");
+            // Avoid anything between script tags
+            Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Avoid anything in a src='...' type of expression
+            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Remove any lonesome </script> tag
+            scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Remove any lonesome <script ...> tag
+            scriptPattern = Pattern.compile("<script(.*?)>",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Avoid eval(...) expressions
+            scriptPattern = Pattern.compile("eval\\((.*?)\\)",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Avoid expression(...) expressions
+            scriptPattern = Pattern.compile("expression\\((.*?)\\)",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Avoid javascript:... expressions
+            scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Avoid vbscript:... expressions
+            scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Avoid οnlοad= expressions
+            scriptPattern = Pattern.compile("onload(.*?)=",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+
+            scriptPattern = Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+
+            scriptPattern = Pattern.compile("</iframe>", Pattern.CASE_INSENSITIVE);
+            value = scriptPattern.matcher(value).replaceAll("");
+            // Remove any lonesome <script ...> tag
+            scriptPattern = Pattern.compile("<iframe(.*?)>",
+                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
+            value = scriptPattern.matcher(value).replaceAll("");
+        }
+        return value;
+    }
+
+    public static String escape(String s) {
+        StringBuilder sb = new StringBuilder(s.length() + 16);
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            switch (c) {
+                case '>':
+                    sb.append('>');// 全角大于号
+                    break;
+                case '<':
+                    sb.append('<');// 全角小于号
+                    break;
+                case '\'':
+                    sb.append('‘');// 全角单引号
+                    break;
+                case '\"':
+                    sb.append('“');// 全角双引号
+                    break;
+                case '\\':
+                    sb.append('\');// 全角斜线
+                    break;
+                case '%':
+                    sb.append('%'); // 全角冒号
+                    break;
+                default:
+                    sb.append(c);
+                    break;
+            }
+
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 将容易引起xss漏洞的半角字符直接替换成全角字符
+     *
+     * @param s
+     * @return
+     */
+    public static String xssEncode(String s) {
+        if (s == null || s.isEmpty()) {
+            return s;
+        }
+
+        String result = stripXSS(s);
+        if (null != result) {
+            result = escape(result);
+        }
+
+        return result;
+    }
+
 }

+ 2 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java

@@ -14,6 +14,8 @@ public enum LoginLogTypeEnum {
     LOGIN_SOCIAL(101), // 使用社交登录
     LOGIN_MOBILE(103), // 使用手机登陆
     LOGIN_SMS(104), // 使用短信登陆
+    LOGIN_DING_APP(105), // 钉钉APP
+    LOGIN_DING_H5(106), // 钉钉H5微应用
 
     LOGOUT_SELF(200),  // 自己主动登出
     LOGOUT_DELETE(202), // 强制退出

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java

@@ -207,4 +207,12 @@ public class AuthController {
         return success(authLoginRespVO);
     }
 
+    @PostMapping("/h5SocialLogin")
+    @PermitAll
+    @Operation(summary = "h5微应用 钉钉快捷登录,code 免登录授权码", description = "适合已经配置了钉钉手机号的用户")
+    public CommonResult<AuthLoginRespVO> h5SocialLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
+        AuthLoginRespVO authLoginRespVO = authService.h5SocialLogin(reqVO);
+        return success(authLoginRespVO);
+    }
+
 }

+ 8 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java

@@ -72,6 +72,14 @@ public interface AdminAuthService {
      */
     AuthLoginRespVO appSocialLogin(@Valid AuthSocialLoginReqVO reqVO);
 
+    /**
+     * h5微应用 钉钉快捷登录,code 免登录授权码
+     *
+     * @param reqVO 登录信息
+     * @return 登录结果
+     */
+    AuthLoginRespVO h5SocialLogin(@Valid AuthSocialLoginReqVO reqVO);
+
     /**
      * 刷新访问令牌
      *

+ 40 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
+import cn.iocoder.yudao.framework.common.util.string.StrUtils;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
 import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
@@ -243,12 +244,50 @@ public class AdminAuthServiceImpl implements AdminAuthService {
                 throw exception(USER_NOT_EXISTS);
             }
             // 创建 Token 令牌,记录登录日志
-            return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL);
+            return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_DING_APP);
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
 
+    @Override
+    public AuthLoginRespVO h5SocialLogin(AuthSocialLoginReqVO reqVO) {
+        // h5页面获取免登录授权码code 后台通过code查询用户信息
+        String validCode = StrUtils.xssEncode(reqVO.getCode());
+        try {
+            // 钉钉用户id
+            String userId = DingtalkUtil.getUserIdByAuthCode(validCode);
+            if (StrUtil.isBlank(userId)) {
+                // 查询不到用户详情 返回 登录失败
+                throw exception(USER_NOT_EXISTS);
+            }
+            // 根据钉钉用户id 查询用户详情
+            OapiV2UserGetResponse.UserGetResponse userDetail = DingtalkUtil.getUserDetail(userId);
+            if (ObjUtil.isEmpty(userDetail)) {
+                // 查询不到用户详情 返回 登录失败
+                throw exception(USER_NOT_EXISTS);
+            }
+            // 工号
+            String jobNumber = userDetail.getJobNumber();
+            // 手机号
+            String mobile = userDetail.getMobile();
+            if (StrUtil.isBlank(mobile)) {
+                throw exception(USER_NOT_EXISTS);
+            }
+            // 查询当前用户表中 此手机号 是否存在
+            AdminUserDO user = userService.getUserByMobile(mobile);
+            if (ObjUtil.isEmpty(user)) {
+                throw exception(USER_NOT_EXISTS);
+            }
+            // 创建 Token 令牌,记录登录日志 h5钉钉微应用登录
+            return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_DING_H5);
+        } catch (Exception e) {
+            // 调用钉钉接口报错 提示 登录失败
+            e.printStackTrace();
+            throw exception(USER_NOT_EXISTS);
+        }
+    }
+
     @VisibleForTesting
     void validateCaptcha(AuthLoginReqVO reqVO) {
         ResponseModel response = doValidateCaptcha(reqVO);

+ 5 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/DingtalkUtil.java

@@ -72,8 +72,11 @@ public class DingtalkUtil {
     /**
      * 通过免登授权码获取用户信息
      */
+    private static String URL_GET_USERINFO_BYAUTHCODE;
     @Value("${dingtalk.URL_GET_USERINFO_BYAUTHCODE}")
-    private String URL_GET_USERINFO_BYAUTHCODE;
+    public void setURLGETUSERINFOBYAUTHCODE(String URLGETUSERINFOBYAUTHCODE) {
+        DingtalkUtil.URL_GET_USERINFO_BYAUTHCODE = URLGETUSERINFOBYAUTHCODE;
+    }
 
     /**
      * 通过UNIONID获取用户信息
@@ -119,7 +122,7 @@ public class DingtalkUtil {
         // 1. 获取access_token
         String accessToken = getAccessToken();
         // 2. 获取用户信息
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo");
+        DingTalkClient client = new DefaultDingTalkClient(URL_GET_USERINFO_BYAUTHCODE);
         OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
         req.setCode(authCode);
         OapiV2UserGetuserinfoResponse rsp = client.execute(req, accessToken);

+ 1 - 0
yudao-server/src/main/resources/application.yaml

@@ -292,6 +292,7 @@ yudao:
       - /admin-api/rq/iot-tree/simple-list
       - /system/auth/social-login
       - /system/auth/appSocialLogin
+      - /system/auth/h5SocialLogin
       - /admin-api/rq/deviceTD/runningTsData #查询时序数据库设备参数
       - /admin-api/rq/deviceTD/runningSpanData #查询时序数据库设备参数
       - /admin-api/pms/iot-main-work-order/timeoutDeviceMainDistances #以公司为维度 统计所有超时保养的设备