数据库相关信息
要想对角色权限进行管理的话,至少需要五张表,无论是一个人拥有一个角色,或者多个角色,都需要五张表
分别是
用户表,角色表,用户角色表,权限表,角色权限表
具体表如下,这里偷懒,不加外键了
ShiroConfig
重中之重的 shiro配置如下:
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
*
* Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*
*/
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login.html");
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth.html");
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/user/login", "anon");
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 :这是一个坑呢,一不小心代码就不好使了;
// ① authc:所有url都必须认证通过才可以访问; ② anon:所有url都都可以匿名访问
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myRealm());
return securityManager;
}
/**
* 身份认证realm; (这个需要自己写,账号密码校验;权限等)
*
* @return
*/
@Bean
public MyRealm myRealm() {
MyRealm myRealm = new MyRealm();
return myRealm;
}
/**
* Shiro生命周期处理器
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
* @return
*/
@Bean
@DependsOn({ "lifecycleBeanPostProcessor" })
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
}
编写认证和授权逻辑
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
@Autowired
private PermissionDao permissionDao;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName=(String) SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
Set<String> roles=new HashSet<String>();
List<Role> rolesByUserName = roleDao.getRolesByUserName(userName);
rolesByUserName
.stream()
.forEach((role) -> {
roles.add(role.getRoleName());
});
List<Permission> permissionsByUserName = permissionDao.getPermissionsByUserName(userName);
permissionsByUserName
.stream()
.forEach((permission) ->{
info.addStringPermission(permission.getPermissionName());
});
info.setRoles(roles);
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("token.getPrincipal:" + token.getPrincipal());
System.out.println("token.getCredentials:" + token.getCredentials());
String userName = token.getPrincipal().toString();
User user = userDao.getUserByUserName(userName);
if (user != null) {
//说明用户存在
// Object principal, Object credentials, String realmName
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
return authcInfo;
} else {
//否则认证失败
return null;
}
}
}
UserController
具体的相关接口如下
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public Map<String, Object> login(@Valid User user, BindingResult bindingResult, HttpSession session) {
Map<String, Object> map = new HashMap<String, Object>();
// 1、JSR303
if (bindingResult.hasErrors()) {
map.put("success", false);
map.put("errorInfo", bindingResult.getFieldError().getDefaultMessage());
return map;
}
// 2、Shiro
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
map.put("success", true);
return map;
} catch (Exception e) {
e.printStackTrace();
map.put("success", false);
map.put("errorInfo", "用户名或者密码错误!");
return map;
}
}
@RequiresPermissions({"select"}) //没有的话 AuthorizationException
@PostMapping("/select")
public Map<String, Object> selectPermission() {
System.out.println("select");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有查看的权力");
return map;
}
@RequiresPermissions({"insert"}) //没有的话 AuthorizationException
@PostMapping("/insert")
public Map<String, Object> insertPermission() {
System.out.println("insert");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有增加的权力");
return map;
}
@RequiresPermissions({"update"}) //没有的话 AuthorizationException
@PostMapping("/update")
public Map<String, Object> updatePermission() {
System.out.println("update");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有更新的权力");
return map;
}
@RequiresPermissions({"delete"}) //没有的话 AuthorizationException
@PostMapping("/delete")
public Map<String, Object> deletePermission() {
System.out.println("delete");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前角色有删除的权力");
return map;
}
@RequiresRoles({"vip"}) //没有的话 AuthorizationException
@PostMapping("/vip")
public Map<String, Object> vipRole() {
System.out.println("vip");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前用户具有 vip 角色");
return map;
}
@RequiresRoles({"ip"}) //没有的话 AuthorizationException
@PostMapping("/ip")
public Map<String, Object> ipRole() {
System.out.println("ip");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前用户具有 ip 角色");
return map;
}
@RequiresRoles({"p"}) //没有的话 AuthorizationException
@PostMapping("/p")
public Map<String, Object> pRole() {
System.out.println("vip");
Map<String, Object> map = new HashMap<String, Object>();
map.put("success", true);
map.put("msg", "当前用户具有 p 角色");
return map;
}
}
对AuthorizationException进行处理
如果没有权限访问资源,控制台会报错,而且会直接返给前台错误,看着十分不舒服。
于是对错误进行了一些处理
@ControllerAdvice
public class NoPermissionException {
@ResponseBody
@ExceptionHandler(UnauthorizedException.class)
public String handleShiroException(Exception ex) {
return "无权限";
}
@ResponseBody
@ExceptionHandler(AuthorizationException.class)
public String AuthorizationException(Exception ex) {
return "权限认证失败";
}
}