• shiro授权


    目录

    1. 添加pom.xml依赖

    2. 添加/修改相关配置文件

        2.1 添加spring-shiro.xml文件(Spring与Shiro集成)添加到main\resources目录下 

        2.2 修改web.xml文件,添加shiroFilter的配置

        2.3 shiro开启注解配置(必须将Shiro注解的开启放置到spring-mvc.xml中(即放在springMVC容        器中加载),不然Shiro注解开启无效!!!)

        2.4 shiro注解权限验证失败不跳转路径问题 解决方案:(springmvc中有一个org.springframewor      k.web.servlet.handler.SimpleMappingExceptionResolver类就可以解决这个问题)

    3. 自定义Realm配置Shiro授权认证

    4. Controller层代码

    5. 前端代码

    6. Shiro标签库

    7. Shiro注解 


    1. 添加pom.xml依赖

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0modelVersion>
    5. <groupId>com.jmhgroupId>
    6. <artifactId>mybatis03artifactId>
    7. <version>1.0-SNAPSHOTversion>
    8. <packaging>warpackaging>
    9. <name>mybatis03 Maven Webappname>
    10. <url>http://www.example.comurl>
    11. <properties>
    12. <spring.version>5.0.2.RELEASEspring.version>
    13. <maven.compiler.source>1.8maven.compiler.source>
    14. <maven.compiler.target>1.8maven.compiler.target>
    15. <jackson.version>2.9.3jackson.version>
    16. <shiro.version>1.2.5shiro.version>
    17. <slf4j.version>1.7.7slf4j.version>
    18. <log4j2.version>2.9.1log4j2.version>
    19. properties>
    20. <dependencies>
    21. <dependency>
    22. <groupId>org.springframeworkgroupId>
    23. <artifactId>spring-coreartifactId>
    24. <version>${spring.version}version>
    25. dependency>
    26. <dependency>
    27. <groupId>org.springframeworkgroupId>
    28. <artifactId>spring-beansartifactId>
    29. <version>${spring.version}version>
    30. dependency>
    31. <dependency>
    32. <groupId>org.springframeworkgroupId>
    33. <artifactId>spring-contextartifactId>
    34. <version>${spring.version}version>
    35. dependency>
    36. <dependency>
    37. <groupId>org.springframeworkgroupId>
    38. <artifactId>spring-ormartifactId>
    39. <version>${spring.version}version>
    40. dependency>
    41. <dependency>
    42. <groupId>org.springframeworkgroupId>
    43. <artifactId>spring-txartifactId>
    44. <version>${spring.version}
    45. version>
    46. dependency>
    47. <dependency>
    48. <groupId>org.springframeworkgroupId>
    49. <artifactId>spring-aspectsartifactId>
    50. <version>${spring.version}version>
    51. dependency>
    52. <dependency>
    53. <groupId>org.springframeworkgroupId>
    54. <artifactId>spring-webartifactId>
    55. <version>${spring.version}version>
    56. dependency>
    57. <dependency>
    58. <groupId>org.springframeworkgroupId>
    59. <artifactId>spring-testartifactId>
    60. <version>${spring.version}version>
    61. dependency>
    62. <dependency>
    63. <groupId>org.springframeworkgroupId>
    64. <artifactId>spring-webmvcartifactId>
    65. <version>${spring.version}version>
    66. dependency>
    67. <dependency>
    68. <groupId>jstlgroupId>
    69. <artifactId>jstlartifactId>
    70. <version>1.2version>
    71. dependency>
    72. <dependency>
    73. <groupId>taglibsgroupId>
    74. <artifactId>standardartifactId>
    75. <version>1.1.2version>
    76. dependency>
    77. <dependency>
    78. <groupId>com.fasterxml.jackson.coregroupId>
    79. <artifactId>jackson-databindartifactId>
    80. <version>${jackson.version}version>
    81. <exclusions>
    82. <exclusion>
    83. <artifactId>jackson-annotationsartifactId>
    84. <groupId>com.fasterxml.jackson.coregroupId>
    85. exclusion>
    86. exclusions>
    87. dependency>
    88. <dependency>
    89. <groupId>com.fasterxml.jackson.coregroupId>
    90. <artifactId>jackson-coreartifactId>
    91. <version>${jackson.version}version>
    92. dependency>
    93. <dependency>
    94. <groupId>com.fasterxml.jackson.coregroupId>
    95. <artifactId>jackson-annotationsartifactId>
    96. <version>${jackson.version}version>
    97. dependency>
    98. <dependency>
    99. <groupId>commons-fileuploadgroupId>
    100. <artifactId>commons-fileuploadartifactId>
    101. <version>1.3.3version>
    102. dependency>
    103. <dependency>
    104. <groupId>org.hibernategroupId>
    105. <artifactId>hibernate-validatorartifactId>
    106. <version>6.0.7.Finalversion>
    107. dependency>
    108. <dependency>
    109. <groupId>junitgroupId>
    110. <artifactId>junitartifactId>
    111. <version>4.12version>
    112. <scope>testscope>
    113. dependency>
    114. <dependency>
    115. <groupId>javax.servletgroupId>
    116. <artifactId>javax.servlet-apiartifactId>
    117. <version>4.0.0version>
    118. <scope>providedscope>
    119. dependency>
    120. <dependency>
    121. <groupId>org.mybatisgroupId>
    122. <artifactId>mybatisartifactId>
    123. <version>3.4.5version>
    124. dependency>
    125. <dependency>
    126. <groupId>com.github.pagehelpergroupId>
    127. <artifactId>pagehelperartifactId>
    128. <version>5.1.2version>
    129. dependency>
    130. <dependency>
    131. <groupId>org.mybatisgroupId>
    132. <artifactId>mybatis-springartifactId>
    133. <version>1.3.1version>
    134. dependency>
    135. <dependency>
    136. <groupId>mysqlgroupId>
    137. <artifactId>mysql-connector-javaartifactId>
    138. <version>5.1.44version>
    139. dependency>
    140. <dependency>
    141. <groupId>org.apache.commonsgroupId>
    142. <artifactId>commons-dbcp2artifactId>
    143. <version>2.1.1version>
    144. dependency>
    145. <dependency>
    146. <groupId>org.apache.commonsgroupId>
    147. <artifactId>commons-pool2artifactId>
    148. <version>2.4.3version>
    149. dependency>
    150. <dependency>
    151. <groupId>org.slf4jgroupId>
    152. <artifactId>slf4j-apiartifactId>
    153. <version>${slf4j.version}version>
    154. dependency>
    155. <dependency>
    156. <groupId>org.slf4jgroupId>
    157. <artifactId>jcl-over-slf4jartifactId>
    158. <version>${slf4j.version}version>
    159. <scope>runtimescope>
    160. <exclusions>
    161. <exclusion>
    162. <artifactId>slf4j-apiartifactId>
    163. <groupId>org.slf4jgroupId>
    164. exclusion>
    165. exclusions>
    166. dependency>
    167. <dependency>
    168. <groupId>org.apache.logging.log4jgroupId>
    169. <artifactId>log4j-slf4j-implartifactId>
    170. <version>${log4j2.version}version>
    171. <exclusions>
    172. <exclusion>
    173. <artifactId>slf4j-apiartifactId>
    174. <groupId>org.slf4jgroupId>
    175. exclusion>
    176. exclusions>
    177. dependency>
    178. <dependency>
    179. <groupId>org.apache.logging.log4jgroupId>
    180. <artifactId>log4j-coreartifactId>
    181. <version>${log4j2.version}version>
    182. dependency>
    183. <dependency>
    184. <groupId>org.apache.logging.log4jgroupId>
    185. <artifactId>log4j-apiartifactId>
    186. <version>${log4j2.version}version>
    187. dependency>
    188. <dependency>
    189. <groupId>org.apache.logging.log4jgroupId>
    190. <artifactId>log4j-webartifactId>
    191. <version>${log4j2.version}version>
    192. dependency>
    193. <dependency>
    194. <groupId>com.lmaxgroupId>
    195. <artifactId>disruptorartifactId>
    196. <version>3.2.0version>
    197. dependency>
    198. <dependency>
    199. <groupId>org.projectlombokgroupId>
    200. <artifactId>lombokartifactId>
    201. <version>1.18.20version>
    202. <scope>providedscope>
    203. dependency>
    204. <dependency>
    205. <groupId>org.apache.shirogroupId>
    206. <artifactId>shiro-coreartifactId>
    207. <version>${shiro.version}version>
    208. dependency>
    209. <dependency>
    210. <groupId>org.apache.shirogroupId>
    211. <artifactId>shiro-webartifactId>
    212. <version>${shiro.version}version>
    213. dependency>
    214. <dependency>
    215. <groupId>org.apache.shirogroupId>
    216. <artifactId>shiro-springartifactId>
    217. <version>${shiro.version}version>
    218. dependency>
    219. dependencies>
    220. <build>
    221. <finalName>shiro02finalName>
    222. <resources>
    223. <resource>
    224. <directory>src/main/javadirectory>
    225. <includes>
    226. <include>**/*.xmlinclude>
    227. includes>
    228. resource>
    229. <resource>
    230. <directory>src/main/resourcesdirectory>
    231. <includes>
    232. <include>jdbc.propertiesinclude>
    233. <include>*.xmlinclude>
    234. includes>
    235. resource>
    236. resources>
    237. <pluginManagement>
    238. <plugins>
    239. <plugin>
    240. <groupId>org.mybatis.generatorgroupId>
    241. <artifactId>mybatis-generator-maven-pluginartifactId>
    242. <version>1.3.2version>
    243. <dependencies>
    244. <dependency>
    245. <groupId>mysqlgroupId>
    246. <artifactId>mysql-connector-javaartifactId>
    247. <version>5.1.44version>
    248. dependency>
    249. dependencies>
    250. <configuration>
    251. <overwrite>trueoverwrite>
    252. configuration>
    253. plugin>
    254. plugins>
    255. pluginManagement>
    256. build>
    257. project>

    2. 添加/修改相关配置文件

    2.1 添加spring-shiro.xml文件(Spring与Shiro集成)添加到main\resources目录下 

    • 需要改的地方已注释表明(改成你自定义的Realm类下方已提供资源)
    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    5. <bean id="shiroRealm" class="com.jmh.shiro.utils.Realm">
    6. <property name="credentialsMatcher">
    7. <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    8. <property name="hashAlgorithmName" value="md5"/>
    9. <property name="hashIterations" value="1024"/>
    10. <property name="storedCredentialsHexEncoded" value="true"/>
    11. bean>
    12. property>
    13. bean>
    14. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    15. <property name="realm" ref="shiroRealm" />
    16. bean>
    17. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    18. <property name="securityManager" ref="securityManager" />
    19. <property name="loginUrl" value="/home/index.shtml"/>
    20. <property name="filterChainDefinitions">
    21. <value>
    22. /user/login=anon
    23. /book/**=authc
    24. /common/**=authc
    25. value>
    26. property>
    27. bean>
    28. <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    29. beans>
    •  同时也要将spring-shiro.xml也要导入到spring.xml文件中
    <import resource="spring-shiro.xml"/>

     2.2 修改web.xml文件,添加shiroFilter的配置

    1. <filter>
    2. <filter-name>shiroFilterfilter-name>
    3. <filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
    4. <init-param>
    5. <param-name>targetFilterLifecycleparam-name>
    6. <param-value>trueparam-value>
    7. init-param>
    8. filter>
    9. <filter-mapping>
    10. <filter-name>shiroFilterfilter-name>
    11. <url-pattern>/*url-pattern>
    12. filter-mapping>

    2.3 shiro开启注解配置(必须将Shiro注解的开启放置到spring-mvc.xml中(即放在springMVC容器中加载),不然Shiro注解开启无效!!!)

    1. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
    2. depends-on="lifecycleBeanPostProcessor">
    3. <property name="proxyTargetClass" value="true">property>
    4. bean>
    5. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    6. <property name="securityManager" ref="securityManager"/>
    7. bean>

    2.4 shiro注解权限验证失败不跳转路径问题 解决方案:(springmvc中有一个org.springframework.web.servlet.handler.SimpleMappingExceptionResolver类就可以解决这个问题)

    1. <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    2. <property name="exceptionMappings">
    3. <props>
    4. <prop key="org.apache.shiro.authz.UnauthorizedException">
    5. unauthorized
    6. prop>
    7. props>
    8. property>
    9. bean>

     3. 自定义Realm配置Shiro授权认证

    1. package com.jmh.shiro.utils;
    2. import com.jmh.shiro.model.User;
    3. import com.jmh.shiro.service.IUserService;
    4. import org.apache.shiro.authc.AuthenticationException;
    5. import org.apache.shiro.authc.AuthenticationInfo;
    6. import org.apache.shiro.authc.AuthenticationToken;
    7. import org.apache.shiro.authc.SimpleAuthenticationInfo;
    8. import org.apache.shiro.authz.AuthorizationInfo;
    9. import org.apache.shiro.authz.SimpleAuthorizationInfo;
    10. import org.apache.shiro.realm.AuthorizingRealm;
    11. import org.apache.shiro.subject.PrincipalCollection;
    12. import org.apache.shiro.util.ByteSource;
    13. import javax.annotation.Resource;
    14. import java.net.Authenticator;
    15. import java.util.Set;
    16. public class Realm extends AuthorizingRealm {
    17. //注入
    18. @Resource
    19. private IUserService iUserService;
    20. /**
    21. * 提供授权信息
    22. * @param pc
    23. * @return
    24. */
    25. @Override
    26. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
    27. //获取用户名
    28. String str = (String) pc.getPrimaryPrincipal();
    29. //根据用户名获取角色
    30. Set roles = iUserService.findRoles(str);
    31. //根据用户名获取权限
    32. Set permissions = iUserService.findPermissions(str);
    33. //将角色和权限信息设置到SimpleAuthorizationInfo
    34. SimpleAuthorizationInfo simpl=new SimpleAuthorizationInfo();
    35. simpl.setRoles(roles);
    36. simpl.setStringPermissions(permissions);
    37. return simpl;
    38. }
    39. /**
    40. * 提供认证信息
    41. * @param token
    42. * @return
    43. * @throws AuthenticationException
    44. */
    45. @Override
    46. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    47. //从令牌中获取用户名
    48. String username = (String)token.getPrincipal();
    49. //通过用户名去数据库获取用户对应信息
    50. User user = iUserService.selectByUserName(username);
    51. //判断用户信息不为空
    52. if(null==user){
    53. throw new RuntimeException("用户信息为空");
    54. }
    55. //保存用户认证信息
    56. SimpleAuthenticationInfo siml=new SimpleAuthenticationInfo(
    57. user.getUsername(),
    58. user.getPassword(),
    59. ByteSource.Util.bytes(user.getSalt()),
    60. this.getName()
    61. );
    62. return siml;
    63. }
    64. }

     4. Controller层代码

    1. package com.jmh.shiro.controller;
    2. import com.jmh.shiro.model.User;
    3. import org.apache.shiro.SecurityUtils;
    4. import org.apache.shiro.authc.UsernamePasswordToken;
    5. import org.apache.shiro.authz.annotation.Logical;
    6. import org.apache.shiro.authz.annotation.RequiresRoles;
    7. import org.apache.shiro.subject.Subject;
    8. import org.hibernate.validator.constraints.EAN;
    9. import org.springframework.stereotype.Controller;
    10. import org.springframework.web.bind.annotation.RequestMapping;
    11. import javax.servlet.http.HttpSession;
    12. @Controller
    13. @RequestMapping("/login")
    14. public class LoginController {
    15. /**
    16. * 点击登录跳转登录页面
    17. */
    18. @RequestMapping("toLogin")
    19. public String toLogin(){
    20. return "login";
    21. }
    22. /**
    23. * 登录验证方法
    24. */
    25. @RequestMapping("/login")
    26. public String login(User user, HttpSession httpSession){
    27. //获取主体
    28. Subject subject = SecurityUtils.getSubject();
    29. //使用登录令牌
    30. UsernamePasswordToken upt=new UsernamePasswordToken(user.getUsername(),user.getPassword());
    31. try {
    32. //使用主体登录
    33. subject.login(upt);
    34. //将用户名保存到作用域
    35. httpSession.setAttribute("msg",user.getUsername());
    36. }catch (RuntimeException e){
    37. throw new RuntimeException("账号或密码错误");
    38. }
    39. //验证成功
    40. return "loginIndex";
    41. }
    42. /**
    43. * 点击退出
    44. */
    45. @RequestMapping("/toLoginOut")
    46. public String toLoginOut(){
    47. //获取主体
    48. Subject subject=SecurityUtils.getSubject();
    49. //使用主体退出
    50. subject.logout();
    51. return "index";
    52. }
    53. /**
    54. * 未认证游客通道
    55. */
    56. @RequestMapping("/toNot")
    57. public String toNot(){
    58. return "loginIndex";
    59. }
    60. /**
    61. * 使用角色跳转页面
    62. */
    63. @RequestMapping("/toGj")
    64. @RequiresRoles(value = {"管理员","高级用户"},logical = Logical.OR)
    65. public String toGj(){
    66. return "gj";
    67. }
    68. }

     5. 前端代码

    •  开启服务器自动打开首页
    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: 蒋明辉
    4. Date: 2022/8/19
    5. Time: 22:56
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    9. <html>
    10. <head>
    11. <title>首页title>
    12. head>
    13. <body>
    14. <h1>成功访问主页h1>
    15. <hr/>
    16. <a href="<%=request.getContextPath()%>/login/toLogin">点击登录a>
    17. body>
    18. html>
    •  登录页面
    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: 蒋明辉
    4. Date: 2022/8/20
    5. Time: 1:18
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    9. <html>
    10. <head>
    11. <title>登录title>
    12. head>
    13. <body>
    14. <div style="color: red">${msg}div>
    15. <form action="<%=request.getContextPath()%>/login/login" method="post">
    16. 账 号: <input type="text" name="username"/><br/>
    17. 密 码: <input type="password" name="password"/><br/>
    18. <input type="submit" value="登录">
    19. form>
    20. body>
    21. html>
    • 登录成功页面

    导入Shiro标签库

    <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> 

    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: 蒋明辉
    4. Date: 2022/8/20
    5. Time: 1:30
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    9. <%@taglib prefix="s" uri="http://shiro.apache.org/tags" %>
    10. <html>
    11. <head>
    12. <title>个人主页title>
    13. head>
    14. <body>
    15. <s:authenticated>
    16. <h1>恭喜${msg}!登录成功h1>
    17. s:authenticated>
    18. <%--根据角色--%>
    19. <s:hasRole name="普通用户">
    20. <a href="#">普通用户a>
    21. s:hasRole>
    22. <s:hasRole name="高级用户">
    23. <a href="#">高级用户a>
    24. s:hasRole>
    25. <s:hasRole name="管理员">
    26. <a href="<%=request.getContextPath()%>/login/toGj">管理员a>
    27. s:hasRole>
    28. <%--<hr/>--%>
    29. <%--根据权限--%>
    30. <s:hasPermission name="书本查询">
    31. <h2>书本查询h2>
    32. s:hasPermission>
    33. <%--未登陆(未认证)--%>
    34. <s:notAuthenticated>
    35. <h2>未认证用户h2>
    36. s:notAuthenticated>
    37. <%--已登录(已认证)--%>
    38. <s:authenticated>
    39. <a href="<%=request.getContextPath()%>/login/toLoginOut">点击我退出a>
    40. s:authenticated>
    41. body>
    42. html>
    •  由于权限不足请求路径抛异常跳转的页面
    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: 蒋明辉
    4. Date: 2022/8/21
    5. Time: 18:08
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    9. <html>
    10. <head>
    11. <title>未授权title>
    12. head>
    13. <body>
    14. <h1>权限不足!!!不可此操作h1>
    15. body>
    16. html>
    •  只能指定的权限才可访问的页面
    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: 蒋明辉
    4. Date: 2022/8/21
    5. Time: 18:04
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    9. <html>
    10. <head>
    11. <title>高级用户管理员title>
    12. head>
    13. <body>
    14. <h1>高级用户,管理员h1>
    15. body>
    16. html>

     6. Shiro标签库

    guest标签 :验证当前用户是否为“访客”,即未认证(包含未记住)的用户
    user标签 :认证通过或已记住的用户

    authenticated标签 :已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在
    notAuthenticated标签 :未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户

    principal 标签 :输出当前用户信息,通常为登录帐号信息 

    hasRole标签 :验证当前用户是否属于该角色 
    lacksRole标签 :与hasRole标签逻辑相反,当用户不属于该角色时验证通过
    hasAnyRole标签 :验证当前用户是否属于以下任意一个角色

    hasPermission标签 :验证当前用户是否拥有指定权限

    lacksPermission标签 :与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过 

    7. Shiro注解 

    @RequiresAuthenthentication:表示当前Subject已经通过login进行身份验证;即 Subject.isAuthenticated()返回 true
    @RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的
    @RequiresGuest:表示当前Subject没有身份验证或者通过记住我登录过,即是游客身份
    @RequiresRoles(value = {"admin","user"},logical = Logical.AND):表示当前Subject需要角色admin和user
    @RequiresPermissions(value = {"user:delete","user:b"},logical = Logical.OR):表示当前Subject需要权限user:delete或者user:b 

  • 相关阅读:
    顺序结构程序设计(python)
    数据结构 每日一练:将带头结点的单链表就地逆置(视频讲解两种方法)
    谷歌浏览器访问端口限制
    默然后台二开的论坛社区APP源码
    金融企业Web应用什么防火墙好?
    Lwip中实现DM9000/DM9003驱动之二
    Java SE 18 新增特性
    基于openlayer展示mapbox样式的矢量切片
    WEB应用防火墙是什么,数据库被篡改了怎么办,数据泄漏了用什么防
    【含文档】基于ssm+jsp的积分商城管理系统(含源码+数据库+lw)
  • 原文地址:https://blog.csdn.net/m0_63300795/article/details/126454019