注意: 由于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),只有读的权限没有写的权限






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