注意: 由于JDK17使用的是Jakarta EE规范,而截止2023年12月29日Shiro2.0还处于(alpha)测试阶段,所以只能使用目前最新的版本shiro1.13,但是Shiro1.13版本目前默认使用的是Java EE规范,所以不能直接引入shiro-spring-boot-web-starter依赖
org.projectlombok lombok org.springframework.boot spring-boot-starter-web org.apache.shiro shiro-spring jakarta 1.13.0 org.apache.shiro shiro-core org.apache.shiro shiro-web org.apache.shiro shiro-core jakarta 1.13.0 org.apache.shiro shiro-web jakarta 1.13.0 org.apache.shiro shiro-core org.springframework.boot spring-boot-starter-test test mysql mysql-connector-java 8.0.33 com.baomidou mybatis-plus-boot-starter 3.5.3.2 
用户表(sys_user)
| Field | Type | Null | Key | Default | Extra | Comment | 
|---|---|---|---|---|---|---|
| user_id | bigint | NO | PRI | NULL | auto_increment | |
| username | varchar(50) | NO | UNI | 登录用户名 | ||
| name | varchar(128) | YES | NULL | 姓名 | ||
| password | varchar(100) | YES | NULL | 密码 | 
角色表(sys_role)
| Field | Type | Null | Key | Default | Extra | Comment | 
|---|---|---|---|---|---|---|
| role_id | bigint | NO | PRI | NULL | auto_increment | |
| role_name | varchar(100) | YES | NULL | 角色名称 | ||
| remark | varchar(100) | YES | NULL | 备注 | 
权限表/菜单表(sys_menu)
| Field | Type | Null | Key | Default | Extra | Comment | 
|---|---|---|---|---|---|---|
| perm_id | bigint unsigned | NO | PRI | NULL | auto_increment | |
| perms | varchar(500) | YES | NULL | 授权(多个用逗号分隔,如:user:list,user:create) | 
用户角色表(sys_user_role)
| Field | Type | Null | Key | Default | Extra | Comment | 
|---|---|---|---|---|---|---|
| id | bigint | NO | PRI | NULL | auto_increment | |
| user_id | bigint | YES | NULL | 用户ID | ||
| role_id | bigint | YES | NULL | 角色ID | 
角色权限表(sys_role_menu)
| Field | Type | Null | Key | Default | Extra | Comment | 
|---|---|---|---|---|---|---|
| id | bigint | NO | PRI | NULL | auto_increment | |
| role_id | bigint | YES | NULL | 角色ID | ||
| perm_id | bigint | YES | NULL | 权限ID | 
server:
  port: 端口号
spring:
  datasource:
    type: com.mysql.cj.jdbc.MysqlDataSource
    username: 数据库账号
    password: 数据库密码
    url: jdbc:mysql://localhost:3306/数据库名?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
mybatis-plus:
  global-config:
    banner: off
logging:
  level:
    org.apache.shiro.authc.AbstractAuthenticator: debug
 
开启shiro注解支持
注意:如果不配置这两货,使用shiro的注解时会失效
@Configuration
public class ShiroConfig {
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}
 配置LifecycleBeanPostProcessor
@Configuration
public class ShiroConfig {
    /**
     * 用于管理shiro组件的生命周期
     * @return
     */
    @Bean("lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
}
 配置SecurityManager
@Configuration
public class ShiroConfig {
    @Bean("securityManager")
    public SecurityManager securityManager(JdbcRealm jdbcRealm) {
        return new DefaultWebSecurityManager(jdbcRealm);
    }
}
 配置Realm
注意1:由于我使用的是内置的JDBCRealm实现认证逻辑的,所以配置了数据源,在springboot的yml文件中配置好数据源,直接注入就好
注意2:认证逻辑可以查看JDBCRealm中的doGetAuthenticationInfo方法,JDBCRealm默认查询的表是users(用户表)、user_roles(用户角色表)、roles_permissions(角色权限表)这三张表。如果你的表名不同,修改对应的sql(以下配置中又修改示例);
注意3:realm可以自定义,也可以选择其他Realm。
    @Bean("jdbcRealm")
    public JdbcRealm jdbcRealm() {
        JdbcRealm jdbcRealm = new JdbcRealm();
        //修改查询用户的sql
        String authenticationQuery = "select password from sys_user where username = ?";
        //修改查询用户角色的sql
        String userRolesQuery = "select t2.role_id from sys_user t1,sys_user_role t2 where t1.user_id = t2.user_id and t1.username = ?";
        //修改查询角色权限的sql
        String permissionsQuery = "select perms from sys_menu t1,sys_role_menu t2 where t1.menu_id = t2.menu_id and role_id = ?";
        //设置允许查询权限(默认是false)
        jdbcRealm.setPermissionsLookupEnabled(true);
        jdbcRealm.setAuthenticationQuery(authenticationQuery);
        jdbcRealm.setUserRolesQuery(userRolesQuery);
        jdbcRealm.setPermissionsQuery(permissionsQuery);
        //设置数据源
        jdbcRealm.setDataSource(dataSource);
        return jdbcRealm;
    }
 配置过滤器链
@Configuration
public class ShiroConfig {
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        Map filterMap = new LinkedHashMap<>();
        filterMap.put("/shiro/first", "anon");//设置不需要认证的url
        filterMap.put("/**","authcBasic");//设置需要Basic方式认证的url
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setGlobalFilters(Arrays.asList("noSessionCreation"));//设置无状态服务(禁用会话)
        return shiroFilter;
    }
}
  整体的配置如下:
@Configuration
public class ShiroConfig {
    @Autowired
    private DataSource dataSource;
    /**
     * 用于管理shiro组件的生命周期
     *
     * @return
     */
    @Bean("lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean("jdbcRealm")
    public JdbcRealm jdbcRealm() {
        JdbcRealm jdbcRealm = new JdbcRealm();
        //修改查询用户的sql
        String authenticationQuery = "select password from sys_user where username = ?";
        //修改查询用户角色的sql
        String userRolesQuery = "select t2.role_id from sys_user t1,sys_user_role t2 where t1.user_id = t2.user_id and t1.username = ?";
        //修改查询角色权限的sql
        String permissionsQuery = "select perms from sys_menu t1,sys_role_menu t2 where t1.menu_id = t2.menu_id and role_id = ?";
        //设置允许查询权限(默认是false)
        jdbcRealm.setPermissionsLookupEnabled(true);
        jdbcRealm.setAuthenticationQuery(authenticationQuery);
        jdbcRealm.setUserRolesQuery(userRolesQuery);
        jdbcRealm.setPermissionsQuery(permissionsQuery);
        //设置数据源
        jdbcRealm.setDataSource(dataSource);
        return jdbcRealm;
    }
    @Bean("securityManager")
    public SecurityManager securityManager(JdbcRealm jdbcRealm) {
        return new DefaultWebSecurityManager(jdbcRealm);
    }
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        Map filterMap = new LinkedHashMap<>();
        filterMap.put("/shiro/anon", "anon");//设置不需要认证的url
        filterMap.put("/**", "authcBasic");//设置需要Basic方式认证的url
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setGlobalFilters(Arrays.asList("noSessionCreation"));//设置无状态服务(禁用会话)
        return shiroFilter;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}
  @ControllerAdvice
@Slf4j
public class ExceptionHandlerConfig {
    @ExceptionHandler(AuthorizationException.class)
    @ResponseBody
    public String handleException(AuthorizationException e) {
        log.info("AuthorizationException was thrown", e);
        return "授权失败";
    }
    @ExceptionHandler(AuthenticationException.class)
    @ResponseBody
    public String handleException(AuthenticationException e) {
        log.info("AuthenticationException was thrown", e);
        return "登录失败";
    }
}
 
@RestController
@RequestMapping("/shiro")
public class ShiroController {
    @GetMapping("/anon")
    public String anon() {
        return "不需要认证授权的url";
    }
    @GetMapping("/authentication")
    public String authentication() {
        return "认证成功";
    }
    @RequiresPermissions("sys:perm:read")
    @GetMapping("/permRead")
    public String permRead() {
        return "授权:读";
    }
    @RequiresPermissions("sys:perm:write")
    @GetMapping("/permWrite")
    public String permWrite() {
        return "授权:写";
    }
    //这里是角色id
    @RequiresRoles("1")
    @GetMapping("/roleSys")
    public String roleSys() {
        return "授权:系统管理员";
    }
    //这里是角色id
    @RequiresRoles("2")
    @GetMapping("/roleCom")
    public String roleCom() {
        return "授权:普通管理员";
    }
    //用户的信息
    @GetMapping("/info")
    public String info() {
        Subject subject = SecurityUtils.getSubject();
        Object principal = subject.getPrincipal();
        boolean role1 = subject.hasRole("1");
        boolean role2 = subject.hasRole("2");
        boolean write = subject.isPermitted("sys:perm:write");
        boolean read = subject.isPermitted("sys:perm:read");
        return "principal :" + principal + " write:" + write + " read:" + read + " role1:" + role1 + " role2:" + role2;
    }
}
 
**注意1:**可以使用postman测试,也可以直接浏览器测试。博主图方便直接浏览器测试了
**注意2:**测试的用户名test002,该用户是普通管理员角色(角色id为2),只有读的权限没有写的权限






 
最后贴出源码,希望给个赞!谢谢