注意: 由于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(); MapfilterMap = 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(); MapfilterMap = 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),只有读的权限没有写的权限
最后贴出源码,希望给个赞!谢谢