文章目录
1. 用户角色权限需要解决的问题及shiro的解决方案
- 灵活配置需要和不需要拦截的页面
shiro提供了过滤器可以灵活配置需要和不需要拦截的页面
- 实现用户认证
shiro整合了HttpSession(web应用)可以保存用户登录的信息。
- 根据当前认证用户加载不同的菜单权限
这个和shiro无关,直接展示用户具有的菜单权限即可
- 根据认证用户的权限,严格控制用户的操作
shiro可以用Realm的doGetAuthorizationInfo()方法做授权,结合shiro的注解(@RequiresPermissions(),@RequiresRoles())做权限控制
- 权限==>角色==>用户配置
可以灵活配置权限角色
2. 过滤器
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//配置默认未登录跳转页面
shiroFilterFactoryBean.setLoginUrl("/user/login");
// 配置未授权跳转页面,filterChainDefinitionMap.put("/user/login2", "perms[2]")
// 注解的未授权直接抛异常,由统一异常处理
// shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 配置不许认证的资源
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/user/tologin", "anon");
// filterChainDefinitionMap.put("/user/login2", "perms[2]");
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/user/loginout", "logout");
// 除了上面所有,其它都需要授权,匹配方式,第一次匹配优先
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
3. 登录并获取菜单权限
@RequestMapping("/tologin")
@ResponseBody
public Map<String, Object> login(Model model, String userName, String password) {
Map<String, Object> map = new HashedMap();
if (StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(password)) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
token.setRememberMe(true);
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
map.put("code", -1);
map.put("msg", "登录失败, 用户名或密码错误");
return map;
}
// 获取角色对应的权限
List<SysPermission> sysPermissionList = sysPermissionService.selectSysPermissionsBySysUserName(userName);
if (sysPermissionList != null && sysPermissionList.size() != 0) {
model.addAttribute("sysPermissionList", sysPermissionList);
model.addAttribute("isSysPermissionList", true);
} else {
model.addAttribute("isSysPermissionList", false);
}
// 当前登录用户
SysUser sysUser = new SysUser();
sysUser.setUserName(userName);
model.addAttribute("sysUser", sysUserService.selectOne(sysUser));
map.put("code", 1);
return map;
} else {
map.put("code", -1);
map.put("msg", "用户名与密码不能为空");
return map;
}
}
subject.login()最后由Realm的doGetAuthenticationInfo()执行,查询数据库获取用户,shiro自动帮我们比对密码是否正确
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = (String) token.getPrincipal();
Example sysUserExample = new Example(SysUser.class);
Example.Criteria criteria = sysUserExample.createCriteria();
criteria.andEqualTo("userName", userName);
SysUser user = sysUserMapper.selectByExample(sysUserExample).get(0);
if (user != null) {
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "xxx");
return authcInfo;
} else {
return null;
}
}
到此为止,网站就具有了认证和非认证的状态,但是仅仅有这两种状态不够,因为现在没法保证具体权限的控制,也就是说只要登录了就可以访问任何权限,需要解决这个问题还要利用shiro的授权控制。
4. shiro授权
shiro的授权操作是借助Realm的doGetAuthorizationInfo()来操作的,这个方法会获取用户对应的所有角色和权限,并且放入shiro中统一管理。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) SecurityUtils.getSubject().getPrincipal();
//根据用户名查询出用户记录
Example sysUserExample = new Example(SysUser.class);
Example.Criteria criteria = sysUserExample.createCriteria();
criteria.andEqualTo("userName", userName);
SysUser user = sysUserMapper.selectOneByExample(sysUserExample);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
List<SysRole> roleList = sysRoleMapper.selectRolesByUserId(user.getId());
Set<String> roles = new HashSet<>();
if (roleList.size() > 0) {
for (SysRole role : roleList) {
roles.add(role.getRoleName());
//根据角色id查询所有资源
List<SysPermission> menuList = sysPermissionMapper.selectMenusByRoleId(role.getId());
for (SysPermission menu : menuList) {
info.addStringPermission(menu.getPermissionName()); // 添加权限
}
}
}
info.setRoles(roles);
return info;
}
上述操作是把用户具有的所有角色权限加载出来了,但是还没设定具体的操作需要哪种权限,可以通过注解或代码的方式设定。
@RequestMapping("/usermanage")
@RequiresPermissions({"用户管理"})
public String userManage() {
// 可以手动判断是否拥有权限,也可以通过注解配合ControllerAdvice
//Subject subject = SecurityUtils.getSubject();
//subject.isPermitted("用户管理");
return "usermanage";
}
注:doGetAuthorizationInfo()方法是需要授权时,也就是解析注解或者执行授权代码时才执行的
5. 总结
上述只列出了大体的思路,详细的配置和实现有需要的请查看githup上的项目