You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
26 KiB

3 weeks ago
��diff --git a/README.md b/README.md
index 91ef8ea..66632ef 100644
--- a/README.md
+++ b/README.md
@@ -135,7 +135,7 @@ crm:
| 405xx | �t �0w“pt!}��k�� | base |
| 500xx | �~d��|/�o-h4Q�t�Qde��k�� | base |
| 60001 | ��1lde�m,l�Y��k�� | base |
-| 61001~61999 | �t�0 v�Y3 ao�?1001 �m�]ne��zO�k'����}“pt!} / 61002 �m Y�g��XHo�o���� / 61003 �t@�_�~zOde / 61004 ��)1�hR��U�SY��P�fȕ�pvx / 61005 ��)1�h�m�]�tf�?/ 61006 ��)1�h�m)[�|ȓ Y�W[�;j�ZR�?/ 61007 ��)1�h�~���Yȕ�pvxÕ,�F^�?| crm-auth |
+| 61001~61999 | �t�0 v�Y3 ao�?1001 �m�]ne��zO�k'����}“pt!} / 61002 �m Y�g��XHo�o���� / 61003 �t@�_�~zOde / 61004 ��)1�hR��U�SY��P�fȕ�pvx / 61005 ��)1�h�m�]�tf�?/ 61006 ��)1�h�m)[�|ȓ Y�W[�;j�ZR�?/ 61007 ��)1�h�~���Yȕ�pvxÕ,�F^ / 61008 ȕ�ppn�R�|��,a�a / 61010 Yt�c�XY��P�fȕ�pvx / 61011 Yt�c�X+hr�[�c�tf�?/ 61012 P�oT�uYt�c�X�m�o�Y / 61013 �t�0YY��\�Qf�%1�S`m,h���o�Vjn�~!S}/u?data e��p6} preLoginToken�?/ 61014 '����}�~��{��[�cQ0��?/ 61015 t��
0�m,[�V / 61016 �m�]ne��zO�k�~⫈h�6[} | crm-auth |
| 62001~62999 | “�V"k�Y3 ao�?2001 �oC�v�ttT�j / 62002 �~��7p�~yO� / 62003 �mAZ6}|m3l=v�m�]�tf�?/ 62004 R��U�X�m�]lu��?/ 62005 �h�RM�ȓ�]�Y�m�]r_"�?/ 62006 R��U�X�oC�v���]�u��k���?| crm-file |
| 63001~63999 | ��HrA]p@iT�?3001 Y��P�fȕ�pvx / 63002 R��U�|+hr�[�c�tf�?/ 63003 P�oT�up@iT�m�o�Y / 63004 R��U�|�m)[�|ȓ
Y�tO�d 0 / 63005 R��U�|�m�]�tf�&1�W�[�c�N"�?/ 63006 p@iT+hr�R4U���]2� / 63007 p@iTJ�Nq�`�RxV�o?/ 63008 엡�$UpAi��Y�G��� / 63009 p@iT$iExf��[�fde / 63010 p@iT$i� {p:jjn / 63011 R��U�|�m�]�tf�?/ 63012 `mmT�`"�)1 0Y�����i?j{��?| crm-dict |
| 64001~64999 | O��cc�Y�r�S��}64001 Y��P�fȕ�pvx / 64002 �Y�r�`���]2� / 64003 ��)1�h�[�SAnO��cc�Y?/ 64004 �Y�r {p:jjn / 64005 �Y�r4U`m�]An2Zflz�~荨P���c�W / 64006 �Y�r�W[�@jjo Z?/ 64007 V��TYq�m�]�tf���} | crm-rule |
diff --git a/crm-app/src/main/resources/application.yml b/crm-app/src/main/resources/application.yml
index d2a8d7f..1cedbda 100644
--- a/crm-app/src/main/resources/application.yml
+++ b/crm-app/src/main/resources/application.yml
@@ -49,6 +49,7 @@ crm:
dingtalk:
client-id: "dingikqv3al2koirnku7"
client-secret: "VVlS2GaBdibboTi3l80wvXufTaYxlCHWaLDtJKd9APT37td2II-cosFE_-WkVGIU"
+ prelogin-ttl-seconds: 120 # $i��_�~��{��~?preLoginToken ȓ Yefȓ�q}�~�c}��\�|�t?2 R��Uc
file:
minio:
endpoint: ${CRM_MINIO_ENDPOINT:http://127.0.0.1:9000} # P�oT�}f�gCo�~\{yYtX�j�YP� {)�X[4^���0 MinIO�HWompose ���V�`9ppt�jȓ�]�YZ�?minio:9000�?diff --git a/crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java b/crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java
index 11e03c7..734fa36 100644
--- a/crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java
+++ b/crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java
@@ -20,6 +20,7 @@ import java.util.List;
* dingtalk:
* client-id: dingxxxxxx # ��Y�b�[� ���`��Y�g2|"�'1�k Client ID (X�?AppKey)
* client-secret: xxxxxx # Client Secret (X�?AppSecret)
+ * prelogin-ttl-seconds: 120 # $i��_�~��{��~?preLoginToken ȓ Yefȓ�q}�~�c}
* </pre>
*/
@Data
@@ -32,6 +33,9 @@ public class AuthProperties {
/** �h�o;���Pc�(�?URL�Xjn SecurityConfig �i?j{�'�Ʌ�`W��f�q�~� �mEZ�bT��r} */
private List<String> ignoreUrls = new ArrayList<>();
+ /** �h�R�j0��f�0ȓ Yefȓ�q}�~�c}�+l�['����}�Y� 4Z*[�W�t�`,�f�'1�VÓAR7�Y�/b}�i?j{� 2 R��Uc */
+ private int preloginTtlSeconds = 120;
+
private DingTalk dingtalk = new DingTalk();
@Data
diff --git a/crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java b/crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java
index 4f4f17c..b57d42e 100644
--- a/crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java
+++ b/crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java
@@ -14,6 +14,18 @@ public interface AuthConstants {
/** '����}��xOjn Redis �m`��k key S��]}�}\lu��?key�]krm:auth:token:{jti} */
String LOGIN_TOKEN_KEY = "crm:auth:token:";
+ /** |m3l=vY��]�`�~ 2)} key S��]}�}\lu��?key�]krm:auth:session:{userId}:{clientType}�tWalue=0��d�X�t�0l� jti�?*/
+ String SESSION_INDEX_KEY = "crm:auth:session:";
+
+ /** �h�R�j0��f�0 key S��]}�}\lu��?key�]krm:auth:prelogin:{preLoginToken}�"X �t�0
0Y�Θde�O[ETDEL �m� Z!� D�y�txv} */
+ String PRELOGIN_TOKEN_KEY = "crm:auth:prelogin:";
+
+ /** t��
0�m,[�V͓�V�� key S��]}�}\lu��?key�]krm:auth:kicked:{jti}�X�h�t�`,��m+[���t �0w������t�U�Wt��
0�?*/
+ String KICKED_MARK_KEY = "crm:auth:kicked:";
+
+ /** request attribute�3lf�$i5g{�~c#r�t.^}JwtAuthenticationFilter i�([� <NecurityExceptionHandlers �tO}�~yO��i�evxpCi���m��} */
+ String ATTR_KICKED_OFFLINE = "crm.auth.kickedOffline";
+
/** JWT payload�1l�j0��f� xOne�m� ͓�Vv */
String JWT_KEY_JTI = "jti";
@@ -60,4 +72,16 @@ public interface AuthConstants {
/** P�oT�uYt�c�X�m�o�Y�"X���Y 2�Wĕ�0�W��-W*}.��O} */
int CODE_BUILTIN_ROLE_PROTECTED = 61012;
+
+ /** �t�0YY��\�Qf�%1�S`m,h���o�Vjn�~!S}/u?data=LoginResultDTO e��p6} preLoginToken��\qeS��]l�P�]�t�`$i��_�?*/
+ int CODE_DEVICE_ONLINE = 61013;
+
+ /** �h�R�j0��f�0Ó�ref�#XC~ȓ?�[�S�y�t?|mH�� �r}��\6n���]�g'����} */
+ int CODE_PRELOGIN_INVALID = 61014;
+
+ /** t��
0�m,[�V�#XYY��\�Qf�%1�S`m,h���o�Vlu��0a
0Y��j0��f} */
+ int CODE_KICKED_OFFLINE = 61015;
+
+ /** �m�]ne��zO�k�~⫈h�?*/
+ int CODE_UNSUPPORTED_CLIENT_TYPE = 61016;
}
diff --git a/crm-auth/src/main/java/com/crm/auth/controller/AuthController.java b/crm-auth/src/main/java/com/crm/auth/controller/AuthController.java
index 34a50e7..33b375f 100644
--- a/crm-auth/src/main/java/com/crm/auth/controller/AuthController.java
+++ b/crm-auth/src/main/java/com/crm/auth/controller/AuthController.java
@@ -29,8 +29,19 @@ public class AuthController {
@Operation(summary = "��Y�b'����}�X�X.�?O��]�j��1lde�? , tags = "'����}")
@PostMapping("/login/dingtalk")
- public Result<LoginResultDTO> loginByDingTalk(@RequestParam String authCode) {
- return Result.success(authService.login(IdentityTypeEnum.DINGTALK, authCode));
+ public Result<LoginResultDTO> loginByDingTalk(@RequestParam String authCode,
+ @RequestParam(required = false) String clientType) {
+ LoginResultDTO dto = authService.login(IdentityTypeEnum.DINGTALK, authCode, clientType);
+ // �t�`,�P�,�Js�m�]�Y�[�P6r�,l+u data e��p6} preLoginToken��\qeS��]l��[^� ~\�iZ�B�
0Y��g� �]�U�~5h}spec 3.1�?+ return dto.getStatus() == LoginResultDTO.LoginStatus.SUCCESS
+ ? Result.success(dto)
+ : Result.fail(AuthConstants.CODE_DEVICE_ONLINE, "�t�0YY��\�Qf�%1�S`m,h���o�Vjn�~!S}�`$i��_'����}", dto);
+ }
+
+ @Operation(summary = "�~��{�$i��_'����}�X�V preLoginToken ��!2^ÓF���o�V߂9p~\�W'����}�?, tags = "'����}")
+ @PostMapping("/login/dingtalk/confirm")
+ public Result<LoginResultDTO> confirmLogin(@RequestParam String preLoginToken) {
+ return Result.success(authService.confirmLogin(preLoginToken));
}
@Operation(summary = " Z)1"e" , tags = "'����}")
diff --git a/crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java b/crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java
index 432ada4..46b331c 100644
--- a/crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java
+++ b/crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java
@@ -15,9 +15,40 @@ public class LoginResultDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
- @Schema(description = "JWT token�}\�`a����Y�P�eO��0���Y�PT0�0kuthorization: Bearer {token}")
+ @Schema(description = "'����}�5�� �O0}SUCCESS='����}��,a�Y�elEVICE_ONLINE=�t�0YY��\�Qf�%1�S`m,h���o�Vjn�~!S}�[mT�X�~� �t�0�iZ�B�
0Y�?)
+ private LoginStatus status;
+
+ @Schema(description = "JWT token�}\�`a����Y�P�eO��0���Y�PT0�0kuthorization: Bearer {token}��ltatus=DEVICE_ONLINE Ó5g� null")
private String token;
- @Schema(description = ""�&1�W�m!O")
+ @Schema(description = ""�&1�W�m!O��ltatus=DEVICE_ONLINE Ó5g� null")
private UserInfoDTO userInfo;
+
+ @Schema(description = "�h�R�j0��f�0�tktatus=DEVICE_ONLINE �`Q~e��p}S��]l��~��{�$i��_Z��^�V9p�Q�v confirm ���0[_��mAn��Xan�i?j{� 2 R��Uc")
+ private String preLoginToken;
+
+ public static LoginResultDTO success(String token, UserInfoDTO userInfo) {
+ LoginResultDTO dto = new LoginResultDTO();
+ dto.setStatus(LoginStatus.SUCCESS);
+ dto.setToken(token);
+ dto.setUserInfo(userInfo);
+ return dto;
+ }
+
+ public static LoginResultDTO deviceOnline(String preLoginToken) {
+ LoginResultDTO dto = new LoginResultDTO();
+ dto.setStatus(LoginStatus.DEVICE_ONLINE);
+ dto.setPreLoginToken(preLoginToken);
+ return dto;
+ }
+
+ /** '����}�dIp�5�� ?*/
+ public enum LoginStatus {
+
+ /** '����}��,a�Y��[oken + userInfo ȓ
Y� ?*/
+ SUCCESS,
+
+ /** �t�0YY��\�Qf�%1�S`m,h���o�Vjn�~!S}preLoginToken ȓ
Y� wx}�[mT�X�~� �t�0
0Y�?*/
+ DEVICE_ONLINE
+ }
}
diff --git a/crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java b/crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java
index 822ec55..eb22405 100644
--- a/crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java
+++ b/crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java
@@ -1,5 +1,6 @@
package com.crm.auth.security;
+import com.crm.auth.domain.enums.ClientTypeEnum;
import com.crm.base.security.LoginUser;
import lombok.Data;
@@ -27,4 +28,7 @@ public class AuthLoginUser implements LoginUser, Serializable {
/** �oQgQ */
private String avatar;
+
+ /** �~⫈h�6[0}W��fl�f�'1�V^���z��C�W9p1l�m4d:�� �Pg_p?Redis nM}token ��?JSON�Y}JWT payload �m�]+u��m�t���_0}�t�or_s�b� null */
+ private ClientTypeEnum clientType;
}
diff --git a/crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java b/crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java
index e7e6e6e..da699a9 100644
--- a/crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java
+++ b/crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java
@@ -46,6 +46,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
+ // t��
0�m,[�V�t�U�W�ukoken �[�cQ0��X~} kicked ͓�V��`m�]jn -> i�?attribute�?+ // "�?SecurityExceptionHandlers Hg�d�V�m�d�h(��R� �\f�$i5g{�~䈬 �]Je�t�r�X/\R���,|œ��� 3lC~ȓ�q}
+ if (tokenService.isKickedOffline(token)) {
+ request.setAttribute(AuthConstants.ATTR_KICKED_OFFLINE, Boolean.TRUE);
+ }
log.debug("token ͓��YsȓH�� 3lC~��\���Y�P�w�[? {}", request.getRequestURI());
}
}
diff --git a/crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java b/crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java
index ee5d883..69b5da3 100644
--- a/crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java
+++ b/crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java
@@ -1,6 +1,7 @@
package com.crm.auth.security;
import cn.hutool.json.JSONUtil;
+import com.crm.auth.constant.AuthConstants;
import com.crm.base.domain.result.Result;
import com.crm.base.domain.result.ResultCodeEnum;
import jakarta.servlet.http.HttpServletRequest;
@@ -28,6 +29,13 @@ public class SecurityExceptionHandlers implements AuthenticationEntryPoint, Acce
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
+ // t��
0�m,[�V�nWequest attribute "�?JwtAuthenticationFilter i�6[}�+l{•)1Je�t�r�}\�X�~�
���}\�Qf�%1�S`m,h���o�V�j0��f� �]1}
+ // HTTP `m?401�}\2�"�%1�X�~�G^ȓ Y�k 401 �t]�j0��f� f�}
+ if (Boolean.TRUE.equals(request.getAttribute(AuthConstants.ATTR_KICKED_OFFLINE))) {
+ write(response, HttpServletResponse.SC_UNAUTHORIZED,
+ Result.fail(AuthConstants.CODE_KICKED_OFFLINE, "��'1�k�t@�_�[�cjnO�5g�|�t�`,�'����}�~\MP�[m�{�~?));
+ return;
+ }
write(response, HttpServletResponse.SC_UNAUTHORIZED,
Result.fail(ResultCodeEnum.USER_NOT_FIND, "ȓF��j0��f�W'����}�[6�C~ȓ�q}�t�pxV“nb�j0�?));
}
diff --git a/crm-auth/src/main/java/com/crm/auth/service/IAuthService.java b/crm-auth/src/main/java/com/crm/auth/service/IAuthService.java
index 1a49370..97792d1 100644
--- a/crm-auth/src/main/java/com/crm/auth/service/IAuthService.java
+++ b/crm-auth/src/main/java/com/crm/auth/service/IAuthService.java
@@ -10,9 +10,18 @@ import com.crm.auth.domain.enums.IdentityTypeEnum;
public interface IAuthService {
/**
- * �m Y�g����r'����}�[kuthCode ��m�{“-Wde��?-> V�R_�S/ Z%1=Uȓ�tn"�&1�W -> b~u엡�$U -> �~�`B_ token
+ * �m Y�g����r'����}�[kuthCode ��m�{“-Wde��?-> V�R_�S/ Z%1=Uȓ�tn"�&1�W -> b~u엡�$U -> W��fl�f�'1�V�Y� 4Z?-> �~�`B_ token��?+ * <p>�t?("�&1�W, �~? �[�SAn2Z��0}�t�oi�m�]7��YE�!} token��\Q~e�?status=DEVICE_ONLINE + preLoginToken��\qeS��]l�P�]�tē��`$i��_</p>
+ *
+ * @param clientType �~⫈h�6[}pc/mobile�}\G0Op�_SU�m�]qf���q}��m {|m?�~p����i?j{� PC��\jo Z�f� |Q�Y 61016
*/
- LoginResultDTO login(IdentityTypeEnum type, String authCode);
+ LoginResultDTO login(IdentityTypeEnum type, String authCode, String clientType);
+
+ /**
+ * �~��{�$i��_'����}�8kETDEL Z#X�W�m� Z!� ?preLoginToken -> ��!2^�t?("�&1�W, �~? ÓB0}�t?-> �~�`B_“?token Ut�USU�~ 2)}��?+ * <p>Y�E�An SUCCESS �m� �~�]�/u?z�VY�O�1}preLoginToken ig�Van/�[�S�y�t?|mH�� �r�|�m� ��?61014</p>
+ */
+ LoginResultDTO confirmLogin(String preLoginToken);
/**
* Z)1"e0��d�X token
diff --git a/crm-base/src/main/java/com/crm/base/domain/result/Result.java b/crm-base/src/main/java/com/crm/base/domain/result/Result.java
index c72215d..8c78f85 100644
--- a/crm-base/src/main/java/com/crm/base/domain/result/Result.java
+++ b/crm-base/src/main/java/com/crm/base/domain/result/Result.java
@@ -62,6 +62,15 @@ public class Result<T> implements Serializable {
return res;
}
+ /**
+ * �o����cm�U�|Ǖ� e��p6}�m,l�Y��HrA]�X��'����}P�,�JsÓ��m|m?preLoginToken��\qeS��]l�P�]�tZ��^;u\m�00|�?+ */
+ public static <T> Result<T> fail(Integer code, String msg, T data) {
+ Result<T> res = fail(code, msg);
+ res.setData(data);
+ return res;
+ }
+
/**
* MyBatis-Plus R��U 05pEx��)�X[4^V�pT��ig�em
*/