From 6b6a728cf9965d54d17b7a55e4cb429763c63803 Mon Sep 17 00:00:00 2001 From: Eric <01714308@yto.net.cn> Date: Tue, 10 Feb 2026 17:24:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/Controller/UserController.java | 37 + sdk/backend/oauth2-login-sdk/README.md | 105 ++ sdk/backend/oauth2-login-sdk/img.png | Bin 0 -> 138325 bytes .../lingniu/sdk/common/convert/Convert.java | 1021 +++++++++++++++++ .../lingniu/sdk/config/SdkRedisConfig.java | 7 - .../sdk/context/PermissionContextHolder.java | 27 + .../sdk/service/PermissionService.java | 158 +++ .../sdk/service/RedisAccessTokenService.java | 198 +++- .../sdk/service/RedisRefreshTokenService.java | 280 ++++- .../java/org/lingniu/sdk/utils/UserUtil.java | 17 + .../org/lingniu/sdk/web/UserController.java | 4 + sdk/frontend/oauth2-login-sdk/README.md | 559 +-------- 12 files changed, 1827 insertions(+), 586 deletions(-) create mode 100644 demo/backend/src/main/java/com/example/demo/Controller/UserController.java create mode 100644 sdk/backend/oauth2-login-sdk/README.md create mode 100644 sdk/backend/oauth2-login-sdk/img.png create mode 100644 sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/common/convert/Convert.java create mode 100644 sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/context/PermissionContextHolder.java create mode 100644 sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/PermissionService.java create mode 100644 sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/utils/UserUtil.java diff --git a/demo/backend/src/main/java/com/example/demo/Controller/UserController.java b/demo/backend/src/main/java/com/example/demo/Controller/UserController.java new file mode 100644 index 0000000..56b0fd0 --- /dev/null +++ b/demo/backend/src/main/java/com/example/demo/Controller/UserController.java @@ -0,0 +1,37 @@ +package org.lingniu.sdk.web; + +import com.alibaba.fastjson2.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.lingniu.sdk.model.base.CommonResult; +import org.lingniu.sdk.model.user.UserInfo; +import org.lingniu.sdk.utils.HttpClientUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RequestMapping("/test") +@RestController +public class UserController { + + private final OAuth2ClientProperties oAuth2ClientProperties; + private final ObjectMapper objectMapper; + + public UserController(OAuth2ClientProperties oAuth2ClientProperties, ObjectMapper objectMapper) { + this.oAuth2ClientProperties = oAuth2ClientProperties; + this.objectMapper = objectMapper; + } + @GetMapping("/getUserInfo") + @PreAuthorize("@ss.hasPermi('user:info')") + public CommonResult getUserInfo(@AuthenticationPrincipal UserInfo userInfo) throws Exception { + return CommonResult.success(userInfo); + } + +} diff --git a/sdk/backend/oauth2-login-sdk/README.md b/sdk/backend/oauth2-login-sdk/README.md new file mode 100644 index 0000000..238e15a --- /dev/null +++ b/sdk/backend/oauth2-login-sdk/README.md @@ -0,0 +1,105 @@ +# 一、使用说明 +1.引入依赖 +```xml + + org.lingniu + oauth2-login-sdk + 1.0-SNAPSHOT + +``` +2.添加配置 +```yaml +spring: + security: + oauth2: + resourceserver: + jwt: + # 资源服务器 认证公钥地址 + jwk-set-uri: http://localhost:8000/oauth2/jwks + client: + registration: + portal: + # 统一登录颁发的client_id + client-id: xxx + # 统一登录颁发的秘钥 + client-secret: xxx + # 当前对接客户端名称 随便填 + client-name: xxx + # 认证类型 使用授权码类型 + authorization-grant-type: authorization_code + # 认证地址 + redirect-uri: http://106.14.217.120/portal-ui/callback + # 权限范围 + scope: + - openid + - profile + # 返回权限 + - perms + provider: idp + + provider: + idp: + # sso登录地址 + authorization-uri: http://106.14.217.120/idp-ui/sso + # token 获取接口 + token-uri: http://localhost:8082/oauth2/token + # 用户信息接口 + user-info-uri: http://localhost:8082/userinfo + # 认证公钥地址 + jwk-set-uri: http://localhost:8082/oauth2/jwks + # 用户信息属性 + user-name-attribute: sub +``` +3. 启动项目 +# 二 、 权限配置 +如果不做额外配置,接入成功后默认所有接口都是登录成功后即可访问,如果需要对接口进行更精确精细化的权限控制,提供了如下注解 + +- @PreAuthorize:方法执行前进行权限检查 +- @PostAuthorize:方法执行后进行权限检查 +- @Secured:类似于 @PreAuthorize +- security提供了许多默认表达式 + +![img.png](img.png) + +结合SpEl表达是进行复杂配置 +```java +@Service +public class HelloService { + @PreAuthorize("principal.username.equals('admin')") + public String hello() { + return "hello"; + } + + @PreAuthorize("principal.username.equals(#abc)") + public String admin(String abc) { + return "admin"; + } + + @Secured({"ROLE_user"}) + public String user() { + return "user"; + } + + @PreAuthorize("#age>98") + public String getAge(Integer age) { + return String.valueOf(age); + } + @PostAuthorize("returnObject == null || returnObject.id%2==0") + public User findUserById(Long id) { + // 根据id查找用户,无论用户是否存在,id是偶数的用户才能获取到结果 + // 实现根据id查找用户的逻辑... + return userRepository.findById(id).orElse(null); + } + @GetMapping("/testPermission1") + @PreAuthorize("@ss.hasPermission('def')") + public String testPermission1() { + return "testPermission1 有权访问"; + } + @GetMapping("/testPermission2") + @PreAuthorize("@ss.hasPermission(#code)") + public String testPermission2(String code) { + return "testPermission2 有权访问"; + } +} +``` + diff --git a/sdk/backend/oauth2-login-sdk/img.png b/sdk/backend/oauth2-login-sdk/img.png new file mode 100644 index 0000000000000000000000000000000000000000..16b5999267cb26d09f475a36cb50480956e95257 GIT binary patch literal 138325 zcmV)eK&HQmP)=MP^d+${w#UfG?CDH1ZWyg-4#IX}Qzc`6w|D8CAli0Cki4DCSa3}-_ zHn50AVizfjO{Ca+18{A3?+(3}yZyg;-`m9k2T+tmI*x_+c|7jU&b)ckzVqJ9>_UGqPQ9pgtwHSI9}e$3$~_yX6IPxF?+9M!&#) z;1aj*H4dZ>u#X=d;SP3ypMJ{y=Tq(*C-lB_laricvBz2RHRcN}2OaVxv!TEP^4Sjl zYOr)m)0X}6?WfnIFHTs$Fedw{IP2`>#5oB`Pp!>evMT$zxRhlHNlzwc%}&XEGAVCf zV%~y;jSCVsFNoi^Ab!V!`1}QlxeJog7ba#bi`h)PC?RWcZ1Qs}SN8lGWB_E5$E`%#KT&9cvXFmo`5pb=exr@>MA>tWJJ*ZSum{wE6LA3*$417smw{ zw~v6cJNTh;bHy%+%~>9oNyxQg)!K#8@$+Mn7e`y3S)HW6i`Ql>UY)mS_2va@H_weY?n!7gs znYF3QSEnssZGCo)by-a2l30=Bf`rU@@m9gH=?h{l^P|(4t)IyYVltXpr0w88O_pfR zT>kF5$t!cFr0$%UxNTg@=226<24woI^WonYBM zF=f}}gS=pmAvnQr*otnILYU+l$Ntw^aq&^*;G%YoAj3sM~C3jqE?v$k5 zX^FX$l5!^|i`#K2dE=AviN~aFoRqw7UYzBb=;Y;Vl9$F>XC~)NOy4-(x^ZI0rtz7Z z=$CksRq&LD&gok^_+hfCV#g$`pOe1nkfzOE89gZ_eNo|C+B zMq!-ZCq7%aqg|6D_;PS$0iK-A7PM(sqZbHiD$w~QBlkz7gZJUs?ZDi`U;gEJ(AmdIgzCYHL_&e7KQN2Tl>m9lM=C6B;pZq}|P>06Jp(>f{9GCDb9 zTv7)0o)O7;!;&`-OWwx3d00yB2#aNyC4E>5ho|IDj?XyDUdFf->yVs{!`5vco|Qj3 zQ}D}otbyrUh9&0i(X<6A*)(wu%*-2@v2mOw>tl987iMf|0;?6M{d#0vsIjCs_pp)XPBNEq- zO4u+WansPGEd!Fb_fFf{%esT|B?X41Zx)=E|Cu~IeOohI^E>!Ym8FxKMjk6!Uc=Hi zlF2qPCTme@&XTya1uK(YcsJgqX$nE%>Xq}OV-~KBnID}nF*;>LeEOKIEyL6E#-`@gVQ$*%GyjFeMsi!VVPS5XKWvt z_QA-seZw+#O-tE89(19mEsajwt7-2OL?-9=iOU(hZb!e&eByx_`Gc&2honDT-qyj7 zfJNO7O5fHub?Zn=K7rT#gzT~DV!9&un-rIJiPzVb#-%QgOQYW9B=>f8=JMaCQ#W%{ z_{x-N@!2C&*AGm}n3R>bmlxfhU7IsIap$Onoue}M%*x2e@*hDYJ)NDJH8FGjpp3je z*_)rp-qt63=Rj*fhaklskc0b42mdLsbVBsSm~Fl)3gdr zyTvxwMecU{G;NA?!+_-M!Is?Ni5YV(nON|fooJn4$r+QLGcO^nSkqKZTVhF@XkABv z6SKGUP0t>bwSHt;E82G`prfADuOQ^}1QHnDxN^VX|Ja1UG#wgmjz9_*xL3Jv1?6NZJOnzn+TDxXp9tvbW!%zU1XT zc7gX3Y?@ZgcKtC;8@`7WXiqI9quFcbB{70 zmTFqLrd`pubU)Oz;ns{X>vHz8-|;^4Ax`oUV`u>*;|1>MAMrNa#MCWgvbGM&*hoA) zjXM3tAy#qyklDJqgC8cFDz>j>(*&y~~=n+999PfXo9d2Raa)p1MWlNQ7!EQm{59G&vYJ24HK zR!x2JomESJ7qe`2#=Q7!l0PavV{TlA=ó!>AOu1b4$C2{J@zmIh@IKHwbXKDP7 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

jO8^2E$?g+^l;-5PTEBO5i&BJsC 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 z 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 DyJO}U literal 0 HcmV?d00001 diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/common/convert/Convert.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/common/convert/Convert.java new file mode 100644 index 0000000..ceb92d0 --- /dev/null +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/common/convert/Convert.java @@ -0,0 +1,1021 @@ +package org.lingniu.sdk.common.convert; + + +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author sdk + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.hasLength(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + if (StringUtils.isEmpty(str)) + { + return new String[] {}; + } + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + case "是": + return true; + case "false": + case "no": + case "0": + case "否": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, StandardCharsets.UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[] || obj instanceof Byte[]) + { + if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else + { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0; i < length; i++) + { + dest[i] = bytes[i]; + } + return str(dest, charset); + } + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/config/SdkRedisConfig.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/config/SdkRedisConfig.java index 8081df3..f760255 100644 --- a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/config/SdkRedisConfig.java +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/config/SdkRedisConfig.java @@ -3,8 +3,6 @@ package org.lingniu.sdk.config; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; -import org.lingniu.sdk.common.redis.RedisCache; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -60,9 +58,4 @@ public class SdkRedisConfig { template.afterPropertiesSet(); return template; } - @Bean - @ConditionalOnMissingBean(name="redisCache") - public RedisCache redisCache(@Qualifier("redisTemplate")RedisTemplate redisTemplate){ - return new RedisCache(redisTemplate); - } } \ No newline at end of file diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/context/PermissionContextHolder.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/context/PermissionContextHolder.java new file mode 100644 index 0000000..078805f --- /dev/null +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package org.lingniu.sdk.context; + +import org.lingniu.sdk.common.convert.Convert; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/PermissionService.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/PermissionService.java new file mode 100644 index 0000000..589cfb6 --- /dev/null +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/PermissionService.java @@ -0,0 +1,158 @@ +package org.lingniu.sdk.service;//package com.ruoyi.framework.web.service; + +import org.lingniu.sdk.constant.Constants; +import org.lingniu.sdk.context.PermissionContextHolder; +import org.lingniu.sdk.model.user.UserInfo; +import org.lingniu.sdk.utils.UserUtil; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.Set; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (!StringUtils.hasText(permission)) + { + return false; + } + UserInfo userInfo = UserUtil.getUserInfo(); + if (userInfo==null || CollectionUtils.isEmpty(userInfo.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(userInfo.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_DELIMITER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + UserInfo userInfo = UserUtil.getUserInfo(); + if (userInfo==null || CollectionUtils.isEmpty(userInfo.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = userInfo.getPermissions(); + for (String permission : permissions.split(Constants.PERMISSION_DELIMITER)) + { + if (hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.hasLength(role)) + { + return false; + } + UserInfo userInfo = UserUtil.getUserInfo(); + if (userInfo==null || CollectionUtils.isEmpty(userInfo.getRoles())) + { + return false; + } + for (String sysRole : userInfo.getRoles()) + { + if (Constants.SUPER_ADMIN.equals(sysRole) || sysRole.equals(StringUtils.trimAllWhitespace(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_DELIMITER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + UserInfo userInfo = UserUtil.getUserInfo(); + if (userInfo==null|| CollectionUtils.isEmpty(userInfo.getRoles())) + { + return false; + } + for (String role : roles.split(Constants.ROLE_DELIMITER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trimAllWhitespace(permission)); + } +} diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisAccessTokenService.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisAccessTokenService.java index fa3a1fc..cb711fd 100644 --- a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisAccessTokenService.java +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisAccessTokenService.java @@ -1,89 +1,225 @@ package org.lingniu.sdk.service; import lombok.extern.slf4j.Slf4j; -import org.lingniu.sdk.common.redis.RedisCache; import org.lingniu.sdk.constant.CacheConstants; import org.lingniu.sdk.model.token.AccessTokenInfo; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.time.Instant; import java.util.Map; +import java.util.concurrent.TimeUnit; @Component @Slf4j public class RedisAccessTokenService { - @Autowired - private RedisCache redisCache; - - private final long ACCESS_TOKEN_EXPIRE = 3600; // 1小时 + private final RedisTemplate redisTemplate; + public RedisAccessTokenService(RedisTemplate sdkRedisTemplate) { + this.redisTemplate = sdkRedisTemplate; + } /** * 存储Access Token到Redis */ public void storeAccessToken(AccessTokenInfo tokenInfo) { + if (tokenInfo == null || tokenInfo.getTokenValue() == null) { + log.error("tokenInfo或tokenValue为空"); + return; + } + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, tokenInfo.getTokenValue()); try { - redisCache.setCacheMap(key,tokenInfo.toMap()); + Map tokenMap = tokenInfo.toMap(); + + // 使用Hash类型存储 + redisTemplate.opsForHash().putAll(key, tokenMap); + + // 设置过期时间 Instant expiresAt = tokenInfo.getExpiresAt(); - long expire = ACCESS_TOKEN_EXPIRE; - if(expiresAt!=null){ - expire = expiresAt.getEpochSecond() - Instant.now().getEpochSecond(); + // 1小时 + long expireSeconds = 3600; + if (expiresAt != null) { + expireSeconds = expiresAt.getEpochSecond() - Instant.now().getEpochSecond(); + if (expireSeconds <= 0) { + log.warn("token已过期,不再存储: {}", tokenInfo.getTokenValue()); + redisTemplate.delete(key); // 删除可能已存在的key + return; + } } - redisCache.expire(key,expire); + redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS); + log.debug("存储Access Token成功: key={}, expireSeconds={}", key, expireSeconds); + } catch (Exception e) { - log.error("存储Access Token失败", e); + log.error("存储Access Token失败: token={}", tokenInfo.getTokenValue(), e); } } - + /** * 验证Access Token */ public boolean validateAccessToken(String token) { - String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); - if(!redisCache.hasKey(key)){ + if (token == null || token.trim().isEmpty()) { return false; } - AccessTokenInfo accessTokenInfo = getAccessTokenInfo(token); - if(accessTokenInfo==null){ - return false; - } - return accessTokenInfo.isValid(); - } + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); + + // 检查key是否存在 + Boolean exists = redisTemplate.hasKey(key); + if (!exists) { + return false; + } + + // 获取token信息并验证 + AccessTokenInfo accessTokenInfo = getAccessTokenInfo(token); + return accessTokenInfo != null && accessTokenInfo.isValid(); + } /** * 删除Access Token */ public boolean removeAccessToken(String token) { + if (token == null || token.trim().isEmpty()) { + return false; + } + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); - return redisCache.deleteObject(key); + try { + Boolean deleted = redisTemplate.delete(key); + log.debug("删除Access Token: key={}, 结果={}", key, deleted); + return deleted; + } catch (Exception e) { + log.error("删除Access Token失败: token={}", token, e); + return false; + } } - + /** * 获取Access Token信息 */ public AccessTokenInfo getAccessTokenInfo(String token) { - String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); - Map cacheMap = redisCache.getCacheMap(key); - if(cacheMap!=null){ - return AccessTokenInfo.fromMap(cacheMap); + if (token == null || token.trim().isEmpty()) { + return null; + } + + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); + try { + // 获取所有hash字段 + Map entries = redisTemplate.opsForHash().entries(key); + if (entries.isEmpty()) { + return null; + } + + // 转换为Map + Map cacheMap = convertMap(entries); + return AccessTokenInfo.fromMap(cacheMap); + + } catch (Exception e) { + log.error("获取Access Token信息失败: token={}", token, e); + return null; } - return null; } /** * 作废 删除 - * @param tokenInfo */ public void revokeAccessToken(AccessTokenInfo tokenInfo) { - if(tokenInfo==null){ + if (tokenInfo == null || tokenInfo.getTokenValue() == null) { return; } + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, tokenInfo.getTokenValue()); - redisCache.deleteObject(key); + try { + Boolean deleted = redisTemplate.delete(key); + log.debug("作废Access Token: key={}, 结果={}", key, deleted); + } catch (Exception e) { + log.error("作废Access Token失败: token={}", tokenInfo.getTokenValue(), e); + } + } + + /** + * 刷新Access Token过期时间 + */ + public boolean refreshTokenExpire(String token, long expireSeconds) { + if (token == null || token.trim().isEmpty() || expireSeconds <= 0) { + return false; + } + + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); + try { + Boolean exists = redisTemplate.hasKey(key); + if (exists) { + Boolean refreshed = redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS); + log.debug("刷新Token过期时间: key={}, expireSeconds={}, 结果={}", + key, expireSeconds, refreshed); + return refreshed; + } + return false; + } catch (Exception e) { + log.error("刷新Token过期时间失败: token={}", token, e); + return false; + } + } + + /** + * 获取Token剩余生存时间 + */ + public Long getTokenTtl(String token) { + if (token == null || token.trim().isEmpty()) { + return null; + } + + String key = String.format(CacheConstants.ACCESS_TOKEN_KEY, token); + try { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("获取Token剩余生存时间失败: token={}", token, e); + return null; + } + } + + /** + * 辅助方法:转换Map类型 + */ + @SuppressWarnings("unchecked") + private Map convertMap(Map originalMap) { + // 根据实际情况转换,这里假设键值都是可序列化的对象 + // 如果键都是String,值都是可序列化的对象,可以直接转换 + return (Map) (Map) originalMap; + } + + /** + * 另一种实现方式:使用StringRedisTemplate(如果需要明确的字符串类型) + * + * @Autowired + * private StringRedisTemplate stringRedisTemplate; + * + * 注意:使用StringRedisTemplate时,存储和获取Map需要额外的序列化处理 + */ + + /** + * 批量删除包含特定模式的token(可选功能) + */ + public Long batchRemoveTokens(String pattern) { + if (pattern == null || pattern.trim().isEmpty()) { + return 0L; + } + + try { + // 使用scan命令避免阻塞 + var keys = redisTemplate.keys(pattern); + if (!keys.isEmpty()) { + Long count = redisTemplate.delete(keys); + log.debug("批量删除Token: pattern={}, 删除数量={}", pattern, count); + return count; + } + return 0L; + } catch (Exception e) { + log.error("批量删除Token失败: pattern={}", pattern, e); + return 0L; + } } } \ No newline at end of file diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisRefreshTokenService.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisRefreshTokenService.java index 2454ae5..f4f7aee 100644 --- a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisRefreshTokenService.java +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/service/RedisRefreshTokenService.java @@ -1,81 +1,287 @@ package org.lingniu.sdk.service; import lombok.extern.slf4j.Slf4j; -import org.lingniu.sdk.common.redis.RedisCache; import org.lingniu.sdk.constant.CacheConstants; import org.lingniu.sdk.model.token.RefreshTokenInfo; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import java.time.Instant; import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; @Component @Slf4j public class RedisRefreshTokenService { - @Autowired - private RedisCache redisCache; - - private final long REFRESH_TOKEN_EXPIRE = 30 * 24 * 3600L; // 30天 + private final RedisTemplate redisTemplate; + + public RedisRefreshTokenService(RedisTemplate sdkRedisTemplate) { + this.redisTemplate = sdkRedisTemplate; + } /** * 存储Refresh Token到Redis Hash */ public void storeRefreshToken(RefreshTokenInfo tokenInfo) { - if(tokenInfo==null){ + if (tokenInfo == null || tokenInfo.getTokenValue() == null) { + log.error("tokenInfo或tokenValue为空"); return; } - String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, tokenInfo.getTokenValue()); - - redisCache.setCacheMap(key,tokenInfo.toMap()); - Instant expiresAt = tokenInfo.getExpiresAt(); - long expire = REFRESH_TOKEN_EXPIRE; - if(expiresAt!=null){ - expire = expiresAt.getEpochSecond() - Instant.now().getEpochSecond(); - } - redisCache.expire(key,expire); - // 维护用户会话列表 - String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, tokenInfo.getUsername()); - redisCache.setCacheSet(userSessionsKey,tokenInfo.getTokenValue()); - redisCache.expire(userSessionsKey,expire); + try { + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, tokenInfo.getTokenValue()); + + // 使用Hash存储token信息 + Map tokenMap = tokenInfo.toMap(); + redisTemplate.opsForHash().putAll(key, tokenMap); + + // 设置过期时间 + Instant expiresAt = tokenInfo.getExpiresAt(); + // 30天 + long expireSeconds = 30 * 24 * 3600L; + if (expiresAt != null) { + expireSeconds = expiresAt.getEpochSecond() - Instant.now().getEpochSecond(); + if (expireSeconds <= 0) { + log.warn("Refresh Token已过期,不再存储: {}", tokenInfo.getTokenValue()); + redisTemplate.delete(key); + return; + } + } + + redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS); + log.debug("存储Refresh Token成功: key={}, expireSeconds={}", key, expireSeconds); + + // 维护用户会话列表 - 使用Set存储用户的refresh token + String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, tokenInfo.getUsername()); + redisTemplate.opsForSet().add(userSessionsKey, tokenInfo.getTokenValue()); + redisTemplate.expire(userSessionsKey, expireSeconds, TimeUnit.SECONDS); + + } catch (Exception e) { + log.error("存储Refresh Token失败: token={}", tokenInfo.getTokenValue(), e); + } } - + /** * 获取Refresh Token信息 */ public RefreshTokenInfo getRefreshTokenInfo(String token) { - String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, token); - Map cacheMap = redisCache.getCacheMap(key); - if(cacheMap!=null){ - return RefreshTokenInfo.fromMap(cacheMap); + if (token == null || token.trim().isEmpty()) { + return null; + } + + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, token); + try { + // 获取所有hash字段 + Map entries = redisTemplate.opsForHash().entries(key); + if (entries.isEmpty()) { + return null; + } + + // 转换为Map + Map cacheMap = convertMap(entries); + return RefreshTokenInfo.fromMap(cacheMap); + + } catch (Exception e) { + log.error("获取Refresh Token信息失败: token={}", token, e); + return null; } - return null; } - + /** * 更新Refresh Token最后使用时间 */ public void updateRefreshToken(RefreshTokenInfo tokenInfo) { - if(tokenInfo==null){ + if (tokenInfo == null || tokenInfo.getTokenValue() == null) { + log.error("tokenInfo或tokenValue为空"); return; } - String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, tokenInfo.getTokenValue()); - redisCache.setCacheMap(key,tokenInfo.toUpdateMap()); + + try { + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, tokenInfo.getTokenValue()); + Map updateMap = tokenInfo.toUpdateMap(); + + // 更新指定字段 + for (Map.Entry entry : updateMap.entrySet()) { + redisTemplate.opsForHash().put(key, entry.getKey(), entry.getValue()); + } + + log.debug("更新Refresh Token成功: token={}", tokenInfo.getTokenValue()); + + } catch (Exception e) { + log.error("更新Refresh Token失败: token={}", tokenInfo.getTokenValue(), e); + } } /** - * 作废 删除 - * @param tokenInfo + * 作废/删除Refresh Token */ public void revokeRefreshToken(RefreshTokenInfo tokenInfo) { - if(tokenInfo==null){ + if (tokenInfo == null) { + log.warn("tokenInfo为空"); return; } - String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, tokenInfo.getTokenValue()); - String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, tokenInfo.getUsername()); - redisCache.deleteCacheSetValue(userSessionsKey,tokenInfo.getTokenValue()); - redisCache.deleteObject(key); + + try { + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, tokenInfo.getTokenValue()); + Boolean deleted = redisTemplate.delete(key); + + if (tokenInfo.getUsername() != null) { + // 从用户会话列表中移除 + String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, tokenInfo.getUsername()); + Long removed = redisTemplate.opsForSet().remove(userSessionsKey, tokenInfo.getTokenValue()); + log.debug("从用户会话列表移除token: user={}, token={}, 移除数量={}", + tokenInfo.getUsername(), tokenInfo.getTokenValue(), removed); + } + + log.debug("作废Refresh Token: token={}, 删除结果={}", tokenInfo.getTokenValue(), deleted); + + } catch (Exception e) { + log.error("作废Refresh Token失败: token={}", tokenInfo.getTokenValue(), e); + } + } + + /** + * 验证Refresh Token是否存在 + */ + public boolean validateRefreshToken(String token) { + if (token == null || token.trim().isEmpty()) { + return false; + } + + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, token); + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + log.error("验证Refresh Token失败: token={}", token, e); + return false; + } + } + + /** + * 获取用户的所有Refresh Tokens + */ + public Long getUserRefreshTokensCount(String username) { + if (username == null || username.trim().isEmpty()) { + return 0L; + } + + String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, username); + try { + Long size = redisTemplate.opsForSet().size(userSessionsKey); + return size != null ? size : 0L; + } catch (Exception e) { + log.error("获取用户Refresh Tokens数量失败: user={}", username, e); + return 0L; + } + } + + /** + * 清理用户的所有Refresh Tokens(登出所有设备) + */ + public Long revokeAllUserRefreshTokens(String username) { + if (username == null || username.trim().isEmpty()) { + return 0L; + } + + try { + String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, username); + + // 获取用户的所有token + Set tokens = redisTemplate.opsForSet().members(userSessionsKey); + if (tokens == null || tokens.isEmpty()) { + return 0L; + } + + long deletedCount = 0; + for (Object tokenObj : tokens) { + if (tokenObj instanceof String token) { + String tokenKey = String.format(CacheConstants.REFRESH_TOKEN_KEY, token); + Boolean deleted = redisTemplate.delete(tokenKey); + if (deleted) { + deletedCount++; + } + } + } + + // 删除用户会话列表 + redisTemplate.delete(userSessionsKey); + + log.debug("清理用户所有Refresh Tokens: user={}, 清理数量={}", username, deletedCount); + return deletedCount; + + } catch (Exception e) { + log.error("清理用户所有Refresh Tokens失败: user={}", username, e); + return 0L; + } + } + + /** + * 获取Refresh Token剩余生存时间 + */ + public Long getRefreshTokenTtl(String token) { + if (token == null || token.trim().isEmpty()) { + return null; + } + + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, token); + try { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("获取Refresh Token剩余生存时间失败: token={}", token, e); + return null; + } + } + + /** + * 刷新Refresh Token过期时间 + */ + public boolean refreshTokenExpire(String token, long expireSeconds) { + if (token == null || token.trim().isEmpty() || expireSeconds <= 0) { + return false; + } + + String key = String.format(CacheConstants.REFRESH_TOKEN_KEY, token); + try { + Boolean exists = redisTemplate.hasKey(key); + if (exists) { + Boolean refreshed = redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS); + log.debug("刷新Refresh Token过期时间: token={}, expireSeconds={}, 结果={}", + token, expireSeconds, refreshed); + return refreshed; + } + return false; + } catch (Exception e) { + log.error("刷新Refresh Token过期时间失败: token={}", token, e); + return false; + } + } + + /** + * 辅助方法:转换Map类型 + */ + @SuppressWarnings("unchecked") + private Map convertMap(Map originalMap) { + // 根据实际情况转换,这里假设键值都是可序列化的对象 + return (Map) (Map) originalMap; + } + + /** + * 检查用户会话列表中是否包含指定token + */ + public boolean isTokenInUserSessions(String username, String token) { + if (username == null || token == null) { + return false; + } + + String userSessionsKey = String.format(CacheConstants.USER_SESSIONS, username); + try { + Boolean isMember = redisTemplate.opsForSet().isMember(userSessionsKey, token); + return Boolean.TRUE.equals(isMember); + } catch (Exception e) { + log.error("检查用户会话列表失败: user={}, token={}", username, token, e); + return false; + } } } \ No newline at end of file diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/utils/UserUtil.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/utils/UserUtil.java new file mode 100644 index 0000000..bf80c10 --- /dev/null +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/utils/UserUtil.java @@ -0,0 +1,17 @@ +package org.lingniu.sdk.utils; + +import org.lingniu.sdk.model.user.UserInfo; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +public class UserUtil { + + public static UserInfo getUserInfo(){ + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if(authentication instanceof OAuth2AuthenticationToken oAuth2AuthenticationToken){ + return (UserInfo) oAuth2AuthenticationToken.getPrincipal(); + } + return null; + } +} diff --git a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/web/UserController.java b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/web/UserController.java index c3e3630..22e0d23 100644 --- a/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/web/UserController.java +++ b/sdk/backend/oauth2-login-sdk/src/main/java/org/lingniu/sdk/web/UserController.java @@ -28,6 +28,10 @@ public class UserController { this.oAuth2ClientProperties = oAuth2ClientProperties; this.objectMapper = objectMapper; } + @GetMapping("/getUserInfo") + public CommonResult getUserInfo(@AuthenticationPrincipal UserInfo userInfo) throws Exception { + return CommonResult.success(userInfo); + } @GetMapping("/routes") public CommonResult getUserMenu(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient oAuth2AuthorizedClient) throws Exception { diff --git a/sdk/frontend/oauth2-login-sdk/README.md b/sdk/frontend/oauth2-login-sdk/README.md index 24d28b8..563da32 100644 --- a/sdk/frontend/oauth2-login-sdk/README.md +++ b/sdk/frontend/oauth2-login-sdk/README.md @@ -1,10 +1,8 @@ - ## 安装 - ```bash -npm install unified-login-sdk --save +npm install oauth2-login-sdk --save # 或 -yarn add unified-login-sdk +yarn add oauth2-login-sdk ``` ## 快速开始 @@ -12,522 +10,61 @@ yarn add unified-login-sdk ### 基本使用 ```typescript -import unifiedLoginSDK from 'unified-login-sdk'; +// main.ts +import unifiedLoginSDK from "oauth2-login-sdk" // 初始化配置 unifiedLoginSDK.init({ - clientId: 'your-client-id', - authorizationEndpoint: 'https://auth.example.com/authorize', - tokenEndpoint: 'https://auth.example.com/token', - userInfoEndpoint: 'https://auth.example.com/userinfo', - redirectUri: 'https://your-app.example.com/callback', - storageType: 'localStorage', - autoRefreshToken: true, - tenantId: 'your-tenant-id' // 可选,会自动添加到请求头中的tenant-id字段 -}); - -// 登录 -document.getElementById('login-btn')?.addEventListener('click', () => { - unifiedLoginSDK.login(); -}); - -// 处理回调 -if (unifiedLoginSDK.isAuthenticated()) { - // 已登录,获取用户信息 - unifiedLoginSDK.getUserInfo().then(userInfo => { - console.log('User info:', userInfo); - }); -} else if (unifiedLoginSDK.isCallback()) { - // 处理授权回调 - unifiedLoginSDK.handleCallback().then(userInfo => { - console.log('Login successful:', userInfo); - // 跳转到首页 - window.location.href = '/'; - }).catch(error => { - console.error('Login failed:', error); - }); -} - -// 退出登录 -document.getElementById('logout-btn')?.addEventListener('click', () => { - unifiedLoginSDK.logout().then(() => { - console.log('Logout successful'); - window.location.href = '/login'; - }); -}); + clientId: import.meta.env.VITE_APP_CLIENT_ID, + registrationId: import.meta.env.VITE_APP_REGISTRATION_ID, + storageType: import.meta.env.VITE_APP_STORAGE_TYPE, + basepath: import.meta.env.VITE_APP_BASE_API, + idpLogoutUrl: import.meta.env.VITE_APP_IDP_LOGOUT_URL, + homePage: import.meta.env.VITE_APP_HOME_PAGE +}) +``` +```properties +# 配置文件 +VITE_APP_CLIENT_ID=xxx +VITE_APP_REGISTRATION_ID=xxx +VITE_APP_STORAGE_TYPE=localStorage +VITE_APP_IDP_LOGOUT_URL=http://106.14.217.120/idp-ui/logout +VITE_APP_HOME_PAGE=http://106.14.217.120/portal-ui/index ``` - -## 核心功能 - -### 初始化配置 - ```typescript -unifiedLoginSDK.init({ - clientId: 'your-client-id', - clientSecret: 'your-client-secret', // 可选,某些场景下需要 - authorizationEndpoint: 'https://auth.example.com/authorize', - tokenEndpoint: 'https://auth.example.com/token', - userInfoEndpoint: 'https://auth.example.com/userinfo', - redirectUri: 'https://your-app.example.com/callback', - storageType: 'localStorage', // 可选,默认localStorage - autoRefreshToken: true, // 可选,默认true - permissionsEndpoint: 'https://auth.example.com/permissions' // 可选,权限端点 -}); -``` - -### 登录流程 - -1. 调用`login()`方法跳转到授权页面 -2. 用户在授权页面登录并授权 -3. 授权服务器重定向到配置的`redirectUri` -4. 调用`handleCallback()`方法处理授权回调,获取用户信息 - -### Token管理 - -```typescript -// 获取访问令牌 -const accessToken = unifiedLoginSDK.getAccessToken(); - -// 刷新令牌 -unifiedLoginSDK.refreshToken().then(() => { - console.log('Token refreshed'); -}).catch(error => { - console.error('Failed to refresh token:', error); -}); - -// 检查是否已认证 -const isAuthenticated = unifiedLoginSDK.isAuthenticated(); -``` - -### 用户信息管理 - -```typescript -// 获取用户信息 -unifiedLoginSDK.getUserInfo().then(userInfo => { - console.log('User info:', userInfo); -}); - -// 获取用户权限列表 -unifiedLoginSDK.getPermissions().then(permissions => { - console.log('Permissions:', permissions); -}); -``` - -### 事件监听 - -```typescript -// 监听登录事件 -unifiedLoginSDK.on('login', () => { - console.log('User logged in'); -}); - -// 监听退出事件 -unifiedLoginSDK.on('logout', () => { - console.log('User logged out'); -}); - -// 监听Token过期事件 -unifiedLoginSDK.on('tokenExpired', () => { - console.log('Token expired'); - // 可以在这里执行自定义逻辑,如跳转到登录页 - unifiedLoginSDK.login(); -}); - -// 移除事件监听 -const handleLogin = () => console.log('User logged in'); -unifiedLoginSDK.on('login', handleLogin); -unifiedLoginSDK.off('login', handleLogin); -``` - -## 框架集成 - -### Vue 2 - -```javascript -// main.js -import Vue from 'vue'; -import { createVuePlugin } from 'unified-login-sdk'; -import App from './App.vue'; -import router from './router'; - -// 创建Vue插件 -const vuePlugin = createVuePlugin('localStorage'); - -// 安装插件 -Vue.use(vuePlugin, { - config: { - clientId: 'your-client-id', - authorizationEndpoint: 'https://auth.example.com/authorize', - tokenEndpoint: 'https://auth.example.com/token', - userInfoEndpoint: 'https://auth.example.com/userinfo', - redirectUri: 'https://your-app.example.com/callback' - } -}); - -new Vue({ - router, - render: h => h(App) -}).$mount('#app'); -``` - -在组件中使用: - -```vue - - - -``` - -### Vue 3 - -```javascript -// main.js -import { createApp } from 'vue'; -import { createVuePlugin } from 'unified-login-sdk'; -import App from './App.vue'; -import router from './router'; - -// 创建Vue插件 -const vuePlugin = createVuePlugin('localStorage'); - -const app = createApp(App); - -// 安装插件 -app.use(vuePlugin, { - config: { - clientId: 'your-client-id', - authorizationEndpoint: 'https://auth.example.com/authorize', - tokenEndpoint: 'https://auth.example.com/token', - userInfoEndpoint: 'https://auth.example.com/userinfo', - redirectUri: 'https://your-app.example.com/callback' - } -}); - -app.use(router); -app.mount('#app'); -``` - -在组件中使用(Composition API): - -```vue - - - -``` -``` - -## API参考 - -### 初始化 - -```typescript -init(config: SDKConfig): void -``` - -初始化SDK配置。 - -### 登录 - -```typescript -login(redirectUri?: string): void -``` - -触发登录流程,可选参数`redirectUri`可覆盖初始化时的配置。 - -### 退出登录 - -```typescript -logout(): Promise -``` - -退出登录,清除本地存储的Token和用户信息。 - -### 处理授权回调 - -```typescript -handleCallback(): Promise -``` - -处理授权回调,获取用户信息。 - -### 获取用户信息 - -```typescript -getUserInfo(): Promise -``` - -获取用户基本信息。 - -### 获取用户权限列表 - -```typescript -getPermissions(): Promise -``` - -获取用户权限列表。 - -### 检查是否已认证 - -```typescript -isAuthenticated(): boolean -``` - -检查用户是否已认证。 - -### 获取访问令牌 - -```typescript -getAccessToken(): string | null -``` - -获取访问令牌。 - -### 刷新访问令牌 - -```typescript -refreshToken(): Promise -``` - -刷新访问令牌。 - -### 事件监听 - -```typescript -on(event: 'login' | 'logout' | 'tokenExpired', callback: Function): void -``` - -监听登录、退出或Token过期事件。 - -### 移除事件监听 - -```typescript -off(event: 'login' | 'logout' | 'tokenExpired', callback: Function): void -``` - -移除事件监听。 - -## 配置选项 - -| 选项 | 类型 | 必填 | 默认值 | 描述 | -|------|------|------|--------|------| -| clientId | string | 是 | - | 客户端ID | -| clientSecret | string | 否 | - | 客户端密钥,某些场景下需要 | -| authorizationEndpoint | string | 是 | - | 授权端点URL | -| tokenEndpoint | string | 是 | - | Token端点URL | -| userInfoEndpoint | string | 是 | - | 用户信息端点URL | -| redirectUri | string | 是 | - | 重定向URL | -| storageType | 'localStorage' 'sessionStorage' 'cookie' | 否 | 'localStorage' | Token存储类型 | -| autoRefreshToken | boolean | 否 | true | 是否自动刷新Token | -| permissionsEndpoint | string | 否 | - | 权限端点URL | -| stateLength | number | 否 | 32 | 状态参数长度 | -| tenantId | string | 否 | - | 租户ID,会自动添加到请求头中的tenant-id字段 | - -## 事件处理 - -| 事件 | 描述 | -|------|------| -| login | 用户登录成功时触发 | -| logout | 用户退出登录时触发 | -| tokenExpired | Token过期时触发 | - -## 路由守卫 - -### Vue路由守卫 - -```javascript -// router/index.js -import VueRouter from 'vue-router'; -import { Auth } from 'unified-login-sdk'; -import { Storage } from 'unified-login-sdk'; -import { RouterGuard } from 'unified-login-sdk'; - -const storage = new Storage('localStorage'); -const auth = new Auth(storage); -const routerGuard = new RouterGuard(auth); - -const router = new VueRouter({ - routes: [ - { - path: '/', - name: 'Home', - component: Home - }, - { - path: '/protected', - name: 'Protected', - component: Protected, - meta: { - auth: { - requiresAuth: true, - requiredPermissions: ['read:protected'] +// 配置路由导航守卫 +router.beforeEach(async (to, _from, next) => { + // 打开页面 判断是已认证 + if (!unifiedLoginSDK.isAuthenticated()) { + // 未认证 + if (to.path === '/oauth2/callback') { + // 如果是登录回调 进行回调登录 + await unifiedLoginSDK.handleCallback() + }else{ + // 跳转登录 + await unifiedLoginSDK.login() } - } + } else { + //已认证 打开页面 + next() } - ] -}); +}) -// 添加路由守卫 -router.beforeEach(routerGuard.createVueGuard()); - -export default router; ``` - - - -## 错误处理 - -### 网络错误处理 - ```typescript -try { - await unifiedLoginSDK.getUserInfo(); -} catch (error) { - if (error.name === 'HttpError') { - // 处理HTTP错误 - console.error('HTTP Error:', error.status, error.message); - if (error.status === 401) { - // 未授权,跳转到登录页 - unifiedLoginSDK.login(); - } else if (error.status === 403) { - // 权限不足 - window.location.href = '/403'; +// 请求后端接口添加token +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: import.meta.env.VITE_APP_BASE_API, + // 超时 + timeout: 10000 +}) + +// request拦截器 +import unifiedLoginSDK from "oauth2-login-sdk" +service.interceptors.request.use((config: any) => { + if (getToken() && !isToken) { + config.headers['Authorization'] = unifiedLoginSDK.getToken() } - } else { - // 处理其他错误 - console.error('Error:', error.message); - } -} +}) ``` - -### Token失效处理 - -```typescript -// 监听Token过期事件 -unifiedLoginSDK.on('tokenExpired', () => { - console.log('Token expired'); - // 跳转到登录页 - unifiedLoginSDK.login(); -}); -``` - -## 最佳实践 - -1. **配置安全存储**:根据项目需求选择合适的存储类型,敏感信息建议使用cookie并设置secure和httpOnly标志。 - -2. **合理设置Token过期时间**:根据项目安全性要求设置合适的Token过期时间,建议access token过期时间较短,refresh token过期时间较长。 - -3. **使用路由守卫保护敏感路由**:对需要登录或特定权限的路由使用路由守卫进行保护。 - -4. **处理网络错误**:在调用SDK方法时,使用try-catch捕获并处理可能的错误。 - -5. **监听Token过期事件**:及时处理Token过期情况,避免用户体验下降。 - -6. **不要直接暴露clientSecret**:clientSecret应该只在后端使用,前端SDK尽量避免使用clientSecret。 - -7. **使用HTTPS**:确保所有与授权服务器的通信都使用HTTPS,避免Token被窃取。 - -8. **定期清理存储**:在用户退出登录时,确保清理所有相关存储的信息。 - -## 浏览器兼容性 - -- Chrome (推荐) -- Firefox -- Safari -- Edge - -## 许可证 - -MIT License