【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

Shiro地址:https://shiro.apache.org/
参考:狂神说Java Springboot

文章目录

一、搭建项目

1.1 配置依赖

选择Springboot项目进行搭建,并选择web和thymeleaf模板,将会自动导入spring-boot-starter-thymeleafspring-boot-starter-webspring-boot-starter-test

初始化项目后导入springboot与Shiro整合依赖shiro-spring

pom.xml中的依赖如下:

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.7.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.2 前端页面

通过theamleaf获取数据和页面跳转。项目目录如下:
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

1.2.1 首页index

src/main/resources/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<a th:href="@{user/add}">add</a>  |  <a th:href="@{user/update}">update</a>
</body>
</html>

1.2.2 登录login

src/main/resources/templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
  <p>用户:<input type="text" name="username"></p>
  <p>密码:<input type="password" name="password"></p>
  <p><input type="submit"></p>
</form>
</body>
</html>

1.2.3 其他

其他页面均为空页面,只有一个与文件名相对应的p标签。

二、基本配置

首先搭建最基本的配置代码。

2.1 MyController

控制页面跳转

src/main/java/com/zqc/springbootshiro/controller/MyController.java

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

    @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        model.addAttribute("msg", "hello,Shiro");
        return "index";
    }

    @RequestMapping("/user/add")
    public String add() {
        return "user/add";
    }

    @RequestMapping("/user/update")
    public String update() {
        return "user/update";
    }

    @RequestMapping("/toLogin")
    public String toLogin() {
        return "login";
    }
}

2.2 UserRealm

src/main/java/com/zqc/springbootshiro/config/UserRealm.java

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class UserRealm extends AuthorizingRealm {

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权");
        return null;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行认证");
        return null;
    }
}

2.3 ShiroConfig

src/main/java/com/zqc/springbootshiro/config/ShiroConfig.java

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    // ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        return bean;
    }

    // DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 创建realm对象,需要自定义类
    @Bean(name = "userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }
}

三、登录拦截

3.1 修改ShiroConfig

修改src/main/java/com/zqc/springbootshiro/config/ShiroConfig.java中的getShiroFilterFactoryBean()方法实现登录拦截。

新建一个map用于存储配置信息,并通过bean.setFilterChainDefinitionMap(filterMap);进行设置。

// ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
    ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    // 设置安全管理器
    bean.setSecurityManager(defaultWebSecurityManager);

    // 添加shiro的内置过滤器
    /*
        anon: 无需认证就能访问
        authc:必须认证才能访问
        user:必须拥有 记住我 功能才能用
        perms:拥有对某个资源的权限才能访问
        role:拥有某个角色权限才能访问
     */
    Map<String, String> filterMap = new LinkedHashMap<>();
    // user目录下的文件,必须要认证才能访问。
    filterMap.put("/user/*", "authc");

    bean.setFilterChainDefinitionMap(filterMap);

    // 如果没有权限,设置登录的请求
    bean.setLoginUrl("/toLogin");
    return bean;
}

3.2 测试

运行项目,登录默认地址:http://localhost:8080/
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
点击add或update,将跳转到登录页面。
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

四、用户认证

4.1 修改MyController

src/main/java/com/zqc/springbootshiro/controller/MyController.java内增添一个login()方法。

@RequestMapping("/login")
public String login(String username, String password, Model model) {
    // 获取当前的用户
    Subject subject = SecurityUtils.getSubject();
    // 封装用户的登录数据
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    try {
        subject.login(token); // 执行登录的方法,如果没有异常就说明OK了
        return "index";
    } catch (UnknownAccountException e) {  // 用户名不存在
        model.addAttribute("msg", "用户名错误");
        return "login";
    } catch (IncorrectCredentialsException e) {  // 密码不存在
        model.addAttribute("msg", "密码错误");
        return "login";
    }
}

4.2 修改UserRealm

修改UserRealm方法,模拟数据库传入用户名和密码,并判断是否正确。
src/main/java/com/zqc/springbootshiro/config/UserRealm.java

// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    System.out.println("执行认证");
    // 用户名,密码(正常情况下,从数据库中取出)
    String name = "root";
    String password = "123456";

    UsernamePasswordToken userToken = (UsernamePasswordToken) token;

    if (!userToken.getUsername().equals(name)) {
        return null; // 抛出异常 UnknownAccountException
    }
    return new SimpleAuthenticationInfo("", password,"");
}

4.3 测试

运行项目,登录默认地址:http://localhost:8080/
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
点击add或update,将跳转到登录页面。输入刚刚设置的账户(root)和密码(123456)
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
将跳转到add或update页面
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

五、整合Mybatis完成用户认证

5.1 创建数据库

创建一个简单的数据库,其中包含如下字段,并插入一些数据。
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

5.2 配置依赖

pom.xml新增如下配置文件,分别用于配置lombok、druid、mysql、jdbc和mybatis整合。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.21</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.0</version>
</dependency>

5.3 配置文件

新建src/main/resources/application.yml。用于配置数据库、druid、mybatis,以下内容需要根据自己的配置进行修改:

  • 数据库:用户名、密码、url
  • druid:无
  • mybatis:type-aliases-package、mapper-locations
spring:
  datasource:
    # 一、mysql数据库的配置
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/pm?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    # 二、整合druid
    druid:
      # 1.连接池配置
      # 初始化连接池的连接数量 大小,最小,最大
      initialSize: 5
      minIdle: 5
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 是否缓存preparedStatement,也就是PSCache,官方建议MySQL下建议关闭(个人建议如果想用SQL防火墙,建议打开)
      poolPreparedStatements: true

      # 2.基础监控配置
      web-stat-filter:
        enabled: true
        url-pattern: /*
        #设置不统计哪些URL
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
        session-stat-enable: true
        session-stat-max-count: 100

      # 3.
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: true
        #设置监控页面的登录名和密码
        login-username: admin
        login-password: 123456
        # 可访问
        allow: 127.0.0.1
        # 不可访问
        #deny: 192.168.1.100
  # 四、配置日期格式
  mvc:
    format:
      date: yyyy-MM-dd

# 三、整合mybatis
mybatis:
  # pojo所在目录
  type-aliases-package: com.zqc.springbootshiro.pojo
  # mapper所在目录
  mapper-locations: classpath:mapper/*.xml

5.4 实体类USer

注:idea需要lombok插件。

src/main/java/com/zqc/springbootshiro/pojo/User.java

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

5.5 Mapper

编写mybatis,实现对用户的查询:

src/main/java/com/zqc/springbootshiro/mapper/UserMapper.java

import com.zqc.springbootshiro.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface UserMapper {
    User queryUserByName(String name);
}

src/main/resources/mapper/UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zqc.springbootshiro.mapper.UserMapper">
    <select id="queryUserByName" resultType="com.zqc.springbootshiro.pojo.User">
        select * from mybatis.user where name=#{name}
    </select>
</mapper>

5.6 Service

UserService接口:src/main/java/com/zqc/springbootshiro/service/UserService.java

import com.zqc.springbootshiro.pojo.User;

public interface UserService {
    User queryUSerByName(String name);
}

UserService接口实现类:src/main/java/com/zqc/springbootshiro/service/UserServiceImpl.java

import com.zqc.springbootshiro.mapper.UserMapper;
import com.zqc.springbootshiro.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;

    @Override
    public User queryUSerByName(String name) {
        return userMapper.queryUserByName(name);
    }
}

5.7 测试数据库

编写测试代码,测试以上的功能是否实现。根据指定姓名查询数据库的内容。

src/test/java/com/zqc/springbootshiro/SpringbootShiroApplicationTests.java

@SpringBootTest
class SpringbootShiroApplicationTests {

    @Autowired
    UserServiceImpl userService;

    @Test
    void contextLoads() {
        System.out.println(userService.queryUSerByName("zqc"));
    }
}

如果查询成功继续下面操作。

5.8 修改UserRealm

src/main/java/com/zqc/springbootshiro/config/UserRealm.java

自动装配UserServiceImpl实现类

@Autowired
UserServiceImpl userService;

修改doGetAuthenticationInfo()方法,从数据库中获取用户名和密码。

UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 连接数据库
User user = userService.queryUSerByName(userToken.getUsername());
if (user==null) {
    // 没有这个人
    return null;// 抛出异常 UnknownAccountException
}
// shiro密码认证,加密了。
return new SimpleAuthenticationInfo("", user.getPwd(),"");

修改后代码如下:

import com.zqc.springbootshiro.pojo.User;
import com.zqc.springbootshiro.service.UserServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserServiceImpl userService;
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权");
        return null;
    }

    // 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
	    System.out.println("执行认证");
	    UsernamePasswordToken userToken = (UsernamePasswordToken) token;
	
	    // 连接数据库
	    User user = userService.queryUSerByName(userToken.getUsername());
	
	    if (user==null) {
	        // 没有这个人
	        return null;// 抛出异常 UnknownAccountException
	    }
		// shiro密码认证,加密了。
	    return new SimpleAuthenticationInfo("", user.getPwd(),"");
	}

5.9 测试

打开浏览器:http://localhost:8080/ 进行测试。

数据库中的用户均可登录。

六、请求授权

6.1 MyController

src/main/java/com/zqc/springbootshiro/controller/MyController.java内增添一个unauthorized()方法。

无权限时,显示未经授权,无法访问

@RequestMapping("/noauth")
@ResponseBody
public String unauthorized() {
    return "未经授权,无法访问";
}

6.2 修改ShiroConfig

设置add页面的权限。

当登录时,会执行认证doGetAuthenticationInfo();当访问需要权限的页面时,会执行授权doGetAuthorizationInfo(),但此时并没有给任何用户授权,所以任何用户都无法访问到add页面。

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    // ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        // 添加shiro的内置过滤器
        /*
            anon: 无需认证就能访问
            authc:必须认证才能访问
            user:必须拥有 记住我 功能才能用
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色权限才能访问
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/user/add", "perms[user:add]");
        filterMap.put("/user/update", "perms[user:update]");

        filterMap.put("/user/*", "authc");
        bean.setFilterChainDefinitionMap(filterMap);

        // 如果没有权限,设置登录的请求
        bean.setLoginUrl("/toLogin");

        // 未授权页面
        bean.setUnauthorizedUrl("/noauth");
        return bean;
    }

    // DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 创建realm对象,需要自定义类
    @Bean(name = "userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }
}

访问http://localhost:8080/,登陆后访问add页面:
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

6.3 修改数据库和实体类

增添perms字段(varchar(100)),设置每个用户的权限。后面通过查询数据库中用户的权限,对其权授权。
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
数据库中的数据:
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
同时修改User类,增添perms属性。

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
    private String perms;
}

6.4 修改UserRealm

修改doGetAuthenticationInfo()方法:

修改前:return new SimpleAuthenticationInfo("", user.getPwd(),"");
修改后:return new SimpleAuthenticationInfo(user, user.getPwd(),"");

在认证的时候,将user对象存储进去,后面将拿出来使用。

修改doGetAuthorizationInfo()方法:

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 拿到当前登录用户的对象
Subject subject = SecurityUtils.getSubject();
// 取出上面存储的user
User currentUser = (User) subject.getPrincipal(); 
// 设置当前用户的权限
info.addStringPermission(currentUser.getPerms());  

修改后的代码如下:

import com.zqc.springbootshiro.pojo.User;
import com.zqc.springbootshiro.service.UserServiceImpl;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserServiceImpl userService;
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        // 拿到当前登录用户的对象
        Subject subject = SecurityUtils.getSubject();
        // 取出上面存储的user
        User currentUser = (User) subject.getPrincipal();
        // 设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行认证");
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        // 连接数据库
        User user = userService.queryUSerByName(userToken.getUsername());

        if (user==null) {
            // 没有这个人
            return null;// 抛出异常 UnknownAccountException
        }

        // 可以加密,MD5 MD5盐值加密
        // shiro密码认证,加密了。
        return new SimpleAuthenticationInfo(user, user.getPwd(),"");
    }
}

6.5 测试

同样,访问http://localhost:8080/,登陆后访问add或update页面。登录以下数据库中的账户。
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
zqc用户可访问add页面;root用户可访问update页面;其他用户无法访问任何页面。

七、整合thymeleaf

7.1 依赖

<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

7.2 修改shiroConfig

增加以下内容

// 整合ShiroDialect:用来整合shiro和thymeleaf
@Bean
public ShiroDialect getShiroDialect() {
    return new ShiroDialect();
}

7.3 修改index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>首页</h1>
<hr>
<!--如果用户已经认证了,那么就不在显示登录按钮-->
<div shiro:notAuthenticated="">
    <a th:href="@{/toLogin}">登录</a>
</div>

<p th:text="${msg}"></p>

<!--如果用户有add的访问权限,则显示add标签-->
<diV shiro:hasPermission="user:add">
    <a th:href="@{user/add}">add</a>
</diV>
<!--如果用户有update的访问权限,则显示update标签-->
<div shiro:hasPermission="user:update">
    <a th:href="@{user/update}">update</a>
</div>
</body>
</html>

7.4 测试

此时会根据登录用户的权限来显示对应的a标签。
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

zqc可查看到add的访问标签;root可以查看到update的访问标签。
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)
【Springboot】整合Shiro实现登录拦截、用户认证、请求授权(thymeleaf、mybatis)

上一篇:Shiro初级探索


下一篇:SpringSecurity-安全框架