Springboot整合shiro:第二篇 用户授权
继上一篇 Springboot整合shiro:第一篇 用户验证完成了springboot整合shiro及shiro的用户验证功能,本篇是在第一篇的基础上完成用户授权功能(用户、角色、权限信息见第一篇)的
Shiro用户授权可以使用两种方式来完成:
- 采用注解的方式,如@RequiresRoles 、@RequiresPermissions等
- 配置自定义URL过滤器 (下一篇完成)
注解方式
完成用户授权的方法
在UserRealm中完成用户授权方法doGetAuthorizationInfo
/**
* 用户通过验证后,Shiro可通过该方法对登录用户进行授权 角色 或 权限, 只有先通过用户验证后才会走到用户授权这一步
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
/**
* 获取当前登录用户的权限和角色
*/
Set<String> privileges = userService.listPrivilege(name);
Set<String> roles = userService.listRole(name);
// Shiro配置授权 信息
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
/**
* 把通过service获取到的角色和权限放进去
*/
s.setStringPermissions(privileges);
s.setRoles(roles);
return s;
}
配置DefaultAdvisorAutoProxyCreator
在Shiro配置类ShiroConfiguration中配置bean,这个bean配置一定得配置,不然注解方式授权会无效
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
编写查询角色、权限的代码
UserService中添加方法:
@Override
public Set<String> listPrivilege(String name) {
return userMapper.listPrivilege(name);
}
@Override
public Set<String> listRole(String name) {
return userMapper.listRole(name);
}
UserServiceImpl中编写实现:
@Override
public Set<String> listPrivilege(String name) {
return userMapper.listPrivilege(name);
}
@Override
public Set<String> listRole(String name) {
return userMapper.listRole(name);
}
UserMapper中添加接口:
/**
* 根据用户名查询对应的权限
*
* @param name
* @return
*/
Set<String> listPrivilege(String name);
/**
* 根据用户名查询对应的角色
*
* @param name
* @return
*/
Set<String> listRole(String name);
UserMapper.xml添加查询SQL
<!-- 根据用户名查询权限 -->
<select id="listPrivilege" parameterType="string"
resultType="string">
SELECT p.name FROM privilege p INNER JOIN role_privilege rp
ON rp.privilege_id = p.id
INNER JOIN roles r ON rp.role_id = r.id AND
r.name = (SELECT
r.name FROM roles r INNER JOIN user_role ur ON
ur.role_id = r.id
INNER
JOIN users u ON u.id = ur.user_id AND u.name =
#{name});
</select>
<!-- 根据用户名查询角色 -->
<select id="listRole" parameterType="string" resultType="string">
SELECT
r.name FROM roles r INNER JOIN user_role ur ON ur.role_id = r.id
INNER
JOIN users u ON u.id = ur.user_id AND u.name = #{name};
</select>
编写未授权异常处理类
这个地方需要主要一下的是:本来在ShrioConfiguration类中的shirFilter方法中可以直接配置 “未授权的跳转地址的”如注释掉的部分://未授权访问,跳转的地址setUnauthorizedUrl方法只针对部分过滤器有效,有些过滤器是无效的 // shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
,但是这里有点小坑的是,如果真这样配置是无法跳转的,
原因见:shiro无权限,不跳转到指定页面。setUnauthorizedUrl无效
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面。
shiroFilterFactoryBean.setLoginUrl("/notLogin");
//未授权访问,跳转的地址setUnauthorizedUrl方法只针对部分过滤器有效,有些过滤器是无效的
// shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置映射关系
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
所以需要写一个异常处理类,来完成跳转
package com.xl.practice.springbootshiropractice.shiro;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.NativeWebRequest;
@ControllerAdvice
public class PublicExceptionHandler {
@ExceptionHandler({UnauthorizedException.class})
public String processUnauthenticatedException(NativeWebRequest request,UnauthorizedException e) {
return "redirect:unauthorized";
}
}
Controller中添加相关方法
/**
* @description 获得工资报酬
* @return
*/
@RequiresRoles("TEACHER") //这个注解表示:该方法需要TEACHER这个角色才能访问
@RequestMapping("/gainSalary")
public String gainSalary() {
return "请查收本月工资:6000 RMB";
}
/**
*
* @return
*/
@RequiresPermissions("PLAY_GAME") // 这个注解表示:该方法需要PLAY_GAME这个权限才能访问
@RequestMapping("/playGame")
public String playGame() {
return "正在越塔中。。。";
}
/**
* 角色不对或无权限跳转的地址
* @return
*/
@RequestMapping("/unauthorized")
public String unauthorized() {
return "。。。。。。。。角色不对,没有权限哦";
}
验证
用户-角色-权限的关系参见第一篇:Springboot整合shiro:第一篇 用户验证
首先,验证@RequiresRoles
在TestController中添加的方法:
/**
* @description 获得工资报酬
* @return
*/
@RequiresRoles("TEACHER") //这个注解表示:该方法需要TEACHER这个角色才能访问
@RequestMapping("/gainSalary")
public String gainSalary() {
return "请查收本月工资:6000 RMB";
}
只有TEACHER这个角色可以访问,那么根据当前数据库的角色信息可得:苍老师 (111)可以访问,小炮同学 (666) 无法访问。
@RequiresPermissions与@RequiresRoles类似,就不做验证了
注解方式的弊端
注解方式以硬编码的形式来授权,一旦权限发生变动就需要,修改代码,然后重新部署,这就比较麻烦,不灵活。
所以,使用“配置自定义URL过滤器 ”的方式会更好,见Springboot整合shiro:第三篇 用户授权-配置自定义URL过滤器 实现
本文源码:
https://github.com/michaelXu12/myrop/tree/master/springboot-shiro