Spring Security进阶
阅读原文时间:2021年09月06日阅读:1

1.连接数据库进行数据的验证

Spring Security进行身份验证或者权限控制时,用户名和密码应该要和数据库的进行比较才行,用户的各种信息我们从数据库中去获取,不用自己在代码或者配置文件中写。

案例

1)创建项目

自己创建一个Maven项目

2)导入依赖

<parent>
    <!--Spring boot-->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-parent</artifactId>
    <version>2.0.6.RELEASE</version>
  </parent>

  <dependencies>
    <!--Spring boot Web-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--Spring Security-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!--数据库连接框架JPA-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
  </dependencies>

3)创建启动类

@SpringBootApplication
public class SecurityApplication2 {
    public static void main(String[] args) {
        SpringApplication.run(SecurityApplication2.class, args);
    }
}

4)配置文件

在项目的resources文件的目录下创建一个配置文件:application.properties

#连接数据库
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/你的数据库?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=用户名
spring.datasource.password=密码
#数据库表的生成
spring.jpa.generate-ddl=true
#显示执行的sql语句
spring.jpa.show-sql=true
#使用的数据库
spring.jpa.database=mysql

5)创建类

来创建一个实体层类、dao层接口、service层、控制层

实体类

//这个注解表示这个是实体类,对应数据库中的表,默认实体名就是类名
@Entity
public class UserInfo {

    //这个属性的表中的主键
    @Id
    //主键的生成策略,IDENTITY 主键自增长
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    //用户名
    private String username;
    //密码
    private String password;
    //角色
    private String roles;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }
}

dao层接口

/*
* 继承JpaRepository<UserInfo,Long>接口,泛型一个是实体类,一个是主键类型
* */
public interface UserInfoDao extends JpaRepository<UserInfo,Long> {
    //根据用户名查找用户
    //(温馨提示:想要查找的话方法名要为 findBy属性字段  删除的话 deleteBy属性字段 ...以此类推这样才不报错)
    UserInfo findByUsername(String username);
}

service层

public interface UserInfoService {
    //根据用户名查找用户
    UserInfo findByUsername(String username);
}


@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoDao userInfoDao;

    //根据用户名返回用户
    @Override
    public UserInfo findByUsername(String username) {
        UserInfo user = userInfoDao.findByUsername(username);
        return user;
    }
}

controller层

@RestController
@RequestMapping("/userInfo")
public class UserInfoController {

    @RequestMapping("common")
    //方法执行前验证用户是否有该角色
    @PreAuthorize(value = "hasAnyRole('normal','admin')")
    public String commonUserInfo() {
        return "==测试数据库  有两个角色==";
    }

    @RequestMapping("admin")
    //方法执行前验证是否有该角色
    @PreAuthorize(value = "hasAnyRole('admin')")
    public String adminUserInfo() {
        return "==测试数据库  有一个角色==";
    }
}

这些准备工作都做完之后,我们可以往数据库中插入一些数据,创建一个类,用来往数据库中添加数据的。

@Component
public class InitJdbc {

    @Autowired
    private UserInfoDao userInfoDao;

    //Java自带的注解,程序启动的时候执行该方法,每启动一次执行一次,
    //插入成功了,再启动项目的时候不想再重复插入,可以把@PostConstruct注释掉
    @PostConstruct
    public void init() {
        //密码要加密
        PasswordEncoder pe = new BCryptPasswordEncoder();

        //添加一个用户
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername("wangwu");
        userInfo.setPassword(pe.encode("123456"));
        userInfo.setRoles("normal");
        userInfoDao.save(userInfo);

        //添加一个用户
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setUsername("admin");
        userInfo1.setPassword(pe.encode("admin"));
        userInfo1.setRoles("admin");
        userInfoDao.save(userInfo1);

    }
}

现在就可以启动项目看看数据库中是否插入成功数据了。

接下来做权限的验证==========

来写一个类,实现UserDetailService并实现它的方法

//把类交给spring容器管理
@Component
public class SecurityDetail implements UserDetailsService {

    @Autowired
    private UserInfoDao userInfoDao;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        UserInfo userInfo = null;

        //UserDetails接口的实现类
        User user = null;
        //判断用户名不为空
        if (s != null) {
            //根据用户名查找用户
            userInfo = userInfoDao.findByUsername(s);
            //判断用户不为空
            if (userInfo != null) {
                //User类的第三个参数是集合,所有创建一个集合,获取用户的角色
                List<GrantedAuthority> list = new ArrayList<>();
                //获取用户的角色,获取到的角色开头一定要以"ROLE_"开头
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + userInfo.getRoles());
                list.add(authority);
                //User的构造器要返回三个参数:用户名、密码、集合的角色
                user = new User(userInfo.getUsername(), userInfo.getPassword(), list);
            }
        }

        return user;
    }
}

再写一个配置类,解析密码的加密方式

@Configuration
@EnableWebSecurity
//开启方法级别的验证
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityDetail securityDetail;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //密码加密的方式
        auth.userDetailsService(securityDetail).passwordEncoder(new BCryptPasswordEncoder());

    }
}

现在可以进行测试了,启动项目,访问控制层的方法,只有方法上标记的角色才能访问该方法

2.认证和授权

authentication:认证,认证访问的用户是不是有效的用户,他是谁。

authorization:授权,访问的用户在系统中能干什么

RBAC:基于角色的访问控制(Role-Based Access Control),用户属于某个角色,而角色拥有某些权限。

权限:能对资源进行操作,比如增删改查

角色:自定义的,表示权限的聚合,一个角色可以有多个权限。

举例说明

设计角色:经理具有数据的修改、删除、查看等;员工只能查看数据。

一个公司中如果想把一个用户设置为经理,只需把他设置为经理这个角色,他就能有修改、删除、查看等操作了,如果公司新来普通员工,只需把他加入到员工这个角色里面就好了。这样,想让什么用户用户什么权限,只需把他加入到相应的角色里就OK了。

UserDetailService:这是一个接口,里面只有一个方法UserDetails loadUserByUsername(String var1),是根据用户名来获取数据库中信息的

主要的实现有:

InMemoryUserDetailsManager在内存中维护用户信息的,使用很方便,可是数据不是持久的

JdbcUserDetailsManager对数据库信息进行操作的,底层是基于jdbcTemplate的,可以使用这个类的方法来操作数据库数据。

UserDetails:提供用户信息的核心接口

// 权限的集合
Collection<? extends GrantedAuthority> getAuthorities();
//获取密码
String getPassword();
//获取用户名
String getUsername();
//用户是否存在
boolean isAccountNonExpired();
//用户是否锁定
boolean isAccountNonLocked();
//证书是否过期
boolean isCredentialsNonExpired();
//账户是否启用
boolean isEnabled();

UserDetails有一个实现类User

//他有两个构造器,参数和UserDetails的字段属性一样
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
   this(username, password, true, true, true, true, authorities);
}

public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
   if (username != null && !"".equals(username) && password != null) {
       this.username = username;
       this.password = password;
       this.enabled = enabled;
       this.accountNonExpired = accountNonExpired;
       this.credentialsNonExpired = credentialsNonExpired;
       this.accountNonLocked = accountNonLocked;
       this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
   } else {
       throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
   }
}

2.1设置表

基于RBAC设置三张表,用户表,角色表,用户和角色的关联表 密码明文分别是 123 456 admin

-- 角色表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` int(11) NOT NULL,
  `rolename` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色名称',
  `rolememo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '角色描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, 'USER', '普通用户');
INSERT INTO `sys_role` VALUES (2, 'READ', '只读');
INSERT INTO `sys_role` VALUES (3, 'ADMIN', '管理员');

SET FOREIGN_KEY_CHECKS = 1;

-- 用户表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `realname` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '真实名字',
  `isenable` int(11) NULL DEFAULT NULL COMMENT '是否开启认证',
  `islock` int(11) NULL DEFAULT NULL COMMENT '是否锁定',
  `isexpire` int(11) NULL DEFAULT NULL,
  `incredentials` int(255) NULL DEFAULT NULL COMMENT '是否过期',
  `createtime` date NULL DEFAULT NULL,
  `logintime` date NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user 密码明文分别是  123   456   admin
-- ----------------------------
INSERT INTO `sys_user` VALUES (6, 'zs', '$2a$10$EGMo2XSdh49cDgXa0OzXYu36HfNssUf7zUDaNIz83AgWveA3GORYq', '张三', 1, 1, 1, 1, '2021-09-02', '2021-09-02');
INSERT INTO `sys_user` VALUES (7, 'lisi', '$2a$10$r9iLBYZzIIt/gyOngvPnZOBZaP4EW58etU1tLPoEh7hlYpydIaM6u', '李四', 1, 1, 1, 1, '2021-09-02', '2021-09-02');
INSERT INTO `sys_user` VALUES (8, 'admin', '$2a$10$P.I3zf7bEAmLmlSwaDOdMOdrxEyTT1QvbqfKC5YGQ7zHk5zUR/dCG', '管理员', 1, 1, 1, 1, '2021-09-02', '2021-09-02');

SET FOREIGN_KEY_CHECKS = 1;

--关联表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `userid` int(11) NOT NULL,
  `roleid` int(11) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (6, 1);
INSERT INTO `sys_user_role` VALUES (7, 2);
INSERT INTO `sys_user_role` VALUES (8, 1);
INSERT INTO `sys_user_role` VALUES (8, 3);

SET FOREIGN_KEY_CHECKS = 1;

2.2创建项目

2.3 导入依赖

把上一个项目的依赖导入进来,再加多一个spring整合mybatis的包

 <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

2.4创建相关类

2.4.1实体类

创建User和Role的实体类,要继承UserDetails

public class SysUser implements UserDetails {

    private Integer id;
    private String username;
    private String password;
    private String realName;
    private boolean isEnable;
    private boolean isExpired;
    private boolean isLock;
    private boolean isCredentials;

    private List<GrantedAuthority> grantedAuthorities;

    private Date createTime;
    private Date loginTime;

    public SysUser() {
    }

    public SysUser(String username, String password, String realName,
                   boolean isEnable, boolean isExpired, boolean isLock,
                   boolean isCredentials, List<GrantedAuthority> grantedAuthorities,
                   Date createTime, Date loginTime) {
        this.username = username;
        this.password = password;
        this.realName = realName;
        this.isEnable = isEnable;
        this.isExpired = isExpired;
        this.isLock = isLock;
        this.isCredentials = isCredentials;
        this.grantedAuthorities = grantedAuthorities;
        this.createTime = createTime;
        this.loginTime = loginTime;
    }

    //角色的集合
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return grantedAuthorities;
    }

    //密码
    @Override
    public String getPassword() {
        return password;
    }

    //用户名
    @Override
    public String getUsername() {
        return username;
    }

    //账号是否存在
    @Override
    public boolean isAccountNonExpired() {
        return isExpired;
    }

    //账号是否锁定
    @Override
    public boolean isAccountNonLocked() {
        return isLock;
    }

    //是否过期
    @Override
    public boolean isCredentialsNonExpired() {
        return isCredentials;
    }

    //是否启用
    @Override
    public boolean isEnabled() {
        return isEnable;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setRealName(String realName) {
        this.realName = realName;
    }

    public void setEnable(boolean enable) {
        isEnable = enable;
    }

    public void setExpired(boolean expired) {
        isExpired = expired;
    }

    public void setLock(boolean lock) {
        isLock = lock;
    }

    public void setCredentials(boolean credentials) {
        isCredentials = credentials;
    }

    public void setGrantedAuthorities(List<GrantedAuthority> grantedAuthorities) {
        this.grantedAuthorities = grantedAuthorities;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public void setLoginTime(Date loginTime) {
        this.loginTime = loginTime;
    }

    public Integer getId() {
        return id;
    }

    public String getRealName() {
        return realName;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public Date getLoginTime() {
        return loginTime;
    }

    @Override
    public String toString() {
        return "SysUser{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", realName='" + realName + '\'' +
                ", isEnable=" + isEnable +
                ", isExpired=" + isExpired +
                ", isLock=" + isLock +
                ", isCredentials=" + isCredentials +
                ", grantedAuthorities=" + grantedAuthorities +
                ", createTime=" + createTime +
                ", loginTime=" + loginTime +
                '}';
    }
}


public class SysRole {

    private Integer id;
    private String role;
    private String rolememo;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getRolememo() {
        return rolememo;
    }

    public void setRolememo(String rolememo) {
        this.rolememo = rolememo;
    }

    @Override
    public String toString() {
        return "SysRole{" +
                "id=" + id +
                ", role='" + role + '\'' +
                ", rolememo='" + rolememo + '\'' +
                '}';
    }
}
2.4.2dao层和对应的xml文件
@Repository
public interface SysRoleMapper {

    //根据用户id查看角色
    List<SysRole> selectByUserId(Integer userId);
}

@Repository
public interface SysUserMapper {

    //插入用户
    int insertSysUser(SysUser sysUser);

    //根据用户名查询用户
    SysUser selectByUsername(String username);
}



<?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.huang.security.mapper.SysRoleMapper">

    <resultMap id="roleMapper" type="com.huang.security.entity.SysRole">
        <id column="id" property="id"/>
        <result column="rolename" property="role"/>
        <result column="rolememo" property="rolememo"/>
    </resultMap>

    <select id="selectByUserId" resultMap="roleMapper" >
        SELECT sr.id,sr.rolename,sr.rolememo FROM sys_role AS sr
        INNER JOIN sys_user_role AS sur ON sr.id = sur.roleid
        WHERE sur.userid = #{userid}
    </select>
</mapper>


<?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.huang.security.mapper.SysUserMapper">

    <resultMap id="userMapper" type="com.huang.security.entity.SysUser">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="realname" property="realName"/>
        <result column="isenable" property="isEnable"/>
        <result column="islock" property="isLock"/>
        <result column="isexpire" property="isExpired"/>
        <result column="incredentials" property="isCredentials"/>
        <result column="createtime" property="createTime"/>
        <result column="logintime" property="loginTime"/>

    </resultMap>

    <insert id="insertSysUser" parameterType="com.huang.security.entity.SysUser">
        insert into sys_user(username,password,realname,isenable,islock,incredentials,createtime,logintime)
         values(#{username},#{password},#{realName},#{isEnable},#{isLock},#{isCredentials},
                #{createTime},#{loginTime})
    </insert>

    <select id="selectByUsername" resultMap="userMapper" >
        select id,username,password,realname,isenable,islock,isexpire,incredentials,createtime,logintime
        from sys_user where username = #{username}
    </select>
</mapper>
2.5service层

service层要实现UserDetailService接口,去获取数据库中的信息做返回

@Service
public class UserWnoRoleService implements UserDetailsService {

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysRoleMapper roleMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名获取用户
        SysUser user = userMapper.selectByUsername(username);

        System.out.println("==== Service =====");
        String roleName = "";
        List<GrantedAuthority> list = new ArrayList<>();

            System.out.println("User" + user);
        if (!StringUtils.isEmpty(user)) {
            //根据用户id获取对应角色
            List<SysRole> roles = roleMapper.selectByUserId(user.getId());

            for (SysRole role : roles) {
                //一个用户可能有多个角色,用集合保存,放到用户的集合里
                roleName = role.getRole();
                GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + roleName);
                list.add(authority);
                user.setGrantedAuthorities(list);
            }

            //返回的这个user是包含角色的
            return user;
        }
        //可以返回自定义user,是因为实体类实现了UserDetails这个接口
        return user;
    }
}
2.6配置文件
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/库名?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=用户名
spring.datasource.password=密码

mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
# 包起别名
mybatis.type-aliases-package=com.huang.security.entity
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
2.7相关的配置类
@Configuration
//@EnableWebSecurity //如果是导入的jar包是spring-boot-starter-security可以不用写
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("======  MySecurityConfig   configure==============");

        //匹配"/index","/login.html","/login"  不用验证(permit 许可),和登录相关的要放行
        http.authorizeRequests().antMatchers("/index","/login.html","/login").permitAll()
                //匹配只有相关角色才能访问的路径
                .antMatchers("/access/user/**").hasRole("USER")
                .antMatchers("/access/read/**").hasRole("READ")
                .antMatchers("/access/admin/**").hasRole("ADMIN")
                //所有都需要验证
                .anyRequest().authenticated()
                //执行结束
                .and()
                //表单的方式登录
                .formLogin()
                //登录的自定义视图页面
                .loginPage("/login.html")
                //登录访问的地址,表单中action的值
                .loginProcessingUrl("/login")
                .and()
                //跨域安全的设置,禁用
                .csrf().disable();
    }

    @Qualifier("userWnoRoleService")
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);
        // userDetailsService使用的是service层的 UserWnoRoleService,它实现了 UserDetailsService
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }
}
2.8html页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    身份验证 <br>
    <a href="/access/user">zs</a> <br>
    <a href="/access/read">lisi</a> <br>
    <a href="/access/admin">admin</a> <br>
    <a href="/logout">退出</a>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>自定义登录页</p>
    <form action="/login" method="post">
        用户名:<input type="text" name="username" value=""><br/>
        密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password" value=""><br/>
        <input type="submit" value="登录">
    </form>
</body>
</html>
2.9controller层测试
@Controller
public class InitController {

    @GetMapping("index")
    public String toIndex() {
        return "forward:/index.html";
    }
}


@RestController
@RequestMapping("/access")
public class UserWnoRoleController {

    @GetMapping("user")
    public String sayUser() {
        return "zs 是 user 角色";
    }

    @GetMapping("read")
    public String sayRead() {
        return "lisi 是 read 角色";
    }

    @GetMapping("admin")
    public String sayAdmin() {
        return "admin 是 user admin 角色";
    }
}

个人笔记