一、Shiro授权流程
承接上一篇博客的内容,我们这回来做shiro的授权
授权可简单理解为who对what(which)进行How操作:
Who,即主体(Subject),What,即资源(Resource),How,权限/许可(Permission)
授权:认证通过后就会进行授权,通过用户名到数据库中查询用户的角色和权限信息然后返回判断
二、Shrio权限案例
1.首先我们在SysMapper中新增两个方法与sql
//根据username查询该用户的所有角色,用于角色验证
Set<String> findRoles(@Param("username") String username);
//根据username查询他所拥有的权限信息,用于权限判断
Set<String> findPermissions(@Param("username") String username);
-------------------------------------------------------------------------
<select id="findRoles" resultType="java.lang.String">
select
r.rolename
from
t_sys_user u,t_sys_user_role ur,t_sys_role r
where
u.userid=ur.userid and ur.roleid=r.roleid
and u.username=#{username}
</select>
<select id="findPermissions" resultType="java.lang.String">
select
p.permission
from
t_sys_user u,t_sys_user_role ur,t_sys_role r,t_sys_role_permission rp,t_sys_permission p
where
u.userid=ur.userid and ur.roleid=r.roleid and r.roleid=rp.roleid and rp.perid=p.perid
and u.username=#{username}
</select>
2.自定义Realm配置Shiro授权认证
package com.zking.ssm.book.shiro;
import com.zking.ssm.book.mapper.SysUserMapper;
import com.zking.ssm.book.model.SysUser;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;
/**
* 自定义Realm的安全数据源,采用数据库的方式
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private SysUserMapper sysUserMapper;
/**
*授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录账号
String username = principalCollection.getPrimaryPrincipal().toString();
//根据username获取用户对应角色
Set<String> roles = sysUserMapper.findRoles(username);
//根据username获取对应的角色权限
Set<String> permissions = sysUserMapper.findPermissions(username);
//创建SimpleAuthorizationInfo
SimpleAuthorizationInfo simple=new SimpleAuthorizationInfo();
//填充用户的角色和权限
simple.setRoles(roles);
simple.setStringPermissions(permissions);
return simple;
}
/**
* 认证
* @param authenticationToken 传入的账号密码令牌Token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取账号
String username = authenticationToken.getPrincipal().toString();
//获取密码
String password = authenticationToken.getCredentials().toString();
//根据账号查询数据库中的用户对象信息
SysUser sysUser = sysUserMapper.userLogin(username);
//判断账号是否存在
if(sysUser ==null){
throw new UnknownAccountException("账号不存在!!!");
}
//创建SimpleAuthenticationInfo,传入正确的账号和密码(来自于数据库)
SimpleAuthenticationInfo simple=new SimpleAuthenticationInfo(
sysUser.getUsername(),
sysUser.getPassword(),
ByteSource.Util.bytes(sysUser.getSalt()),
this.getName()
);
return simple;
}
}
3.简单登录测试
4.使用Shiro标签实现权限验证
1.导入 Shiro 标签库
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
Shiro标签库:
guest标签 :验证当前用户是否为“访客”,即未认证(包含未记住)的用户
user标签 :认证通过或已记住的用户
authenticated标签 :已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在
notAuthenticated标签 :未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户
principal 标签 :输出当前用户信息,通常为登录帐号信息
hasRole标签 :验证当前用户是否属于该角色
lacksRole标签 :与hasRole标签逻辑相反,当用户不属于该角色时验证通过
hasAnyRole标签 :验证当前用户是否属于以下任意一个角色
hasPermission标签 :验证当前用户是否拥有指定权限
lacksPermission标签 :与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过
2.在主页面设置查看权限
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<title>Title</title>
<%@include file="/common/head.jsp"%>
</head>
<body>
<h1>首页${pageContext.request.contextPath}</h1>
<div style="float: right;right: 10px;top: 10px;">
<a href="${pageContext.request.contextPath}/user/userLogout">退出</a>
</div>
<%--<img src="img/fgs.jpg" width="800px" height="500px"/>
<img src="images/yhtx.jpg" width="800px" height="500px"/><br>--%>
<%--相对路径--%>
<a href="${pageContext.request.contextPath}/book/addBook">书本新增</a><br>
<%--绝对路径 加项目路径的方式--%>
<shiro:hasPermission name="bookmanager:book:query">
<a href="${pageContext.request.contextPath}/book/bookList">书本列表</a><br>
</shiro:hasPermission>
<a href="${pageContext.request.contextPath}/tohellow">进入欢迎页面</a><br>
<hr>
<h1>查询返回JSON数据</h1>
<ol>
<shiro:hasRole name="管理员">
<li><a href="${ctx}/book/queryListBooks">查询返回List<T>格式的JSON数据</a></li>
<li><a href="${ctx}/book/querySingleBook?bookId=167">查询返回单个实体格式的JSON数据</a></li>
<li><a href="${ctx}/book/querySingleMap?bookId=15">查询返回Map类型的JSON数据</a></li>
<li><a href="${ctx}/book/queryMapList">查询返回List<Map>的JSON数据</a></li>
<li><a href="${ctx}/book/queryhybridBooks">查询返回JSON混合格式的数据</a></li>
<li><a href="${ctx}/book/queryString">查询返回Sting类型的JSON数据</a></li>
</shiro:hasRole>
</ol>
3.测试
登录zhangsan没有查看权限
5.注解式开发
常用注解
@RequiresAuthenthentication:表示当前Subject已经通过login进行身份验证;即 Subjecj.isAuthenticated()返回 true
@RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的
@RequiresGuest:表示当前Subject没有身份验证或者通过记住我登录过,即是游客身份
@RequiresRoles(value = {"admin","user"},logical = Logical.AND):表示当前Subject需要角色admin和user
@RequiresPermissions(value = {"user:delete","user:b"},logical = Logical.OR):表示当前Subject需要权限user:delete或者user:b
1.在spring-mvc.xml中加入shiro注解式权限
<!--9、配置Shiro注解式权限-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
2.在控制层方法上加上注解
/**
* 返回List泛型格式的JSON数据
* @param book
* @param request
* @return
*/
@RequiresRoles("管理员")
@RequestMapping("/queryListBooks")
@ResponseBody
public List<Book> queryListBooks(Book book,HttpServletRequest request){
PageBean pageBean=new PageBean();
pageBean.setRequest(request);
List<Book> books = bookService.queryBookPager(book, pageBean);
return books;
};
/**
* 返回Map类型的JSON数据
* @param bookId
* @return
*/
@RequiresPermissions("bookmanager:book:edit")
@RequestMapping("/querySingleMap")
@ResponseBody
public Map<String, Object> querySingleMap(Integer bookId){
return bookService.querySingleMap(bookId);
}
3.增加全局异常处理
package com.zking.ssm.book.exception;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import java.util.HashMap;
import java.util.Map;
/**
* SpingMVC提供的第三种种全局异常处理方式
* 1)@ControllerAdvice +@ExceptionHandler
* 2)@RestControllerAdvice +@ExceptionHandler
* @RestControllerAdvice ==@Controller +@ResponseBody 返回JSON的数据格式,绕开视图解析器
*/
//@ControllerAdvice
@RestControllerAdvice
public class GlobalException2{
/*@ExceptionHandler
public ModelAndView exceptionHandler(Exception e){
ModelAndView mv=new ModelAndView();
mv.setViewName("error");
//判断异常类型
if(e instanceof BusinessException){
BusinessException ex=(BusinessException)e;
mv.addObject("msg","系统繁忙,请稍后再试.......");
}
//强制更换视图解析器 不跳页面!!!
mv.setView(new MappingJackson2JsonView());
return mv;
}*/
@ExceptionHandler
public Map<String, Object> exceptionHandler(Exception e){
e.printStackTrace();
Map<String, Object> json=new HashMap();
//判断异常类型
if(e instanceof BusinessException){
json.put("msg","系统繁忙,请稍后再试.......");
json.put("code",500);
}else if(e instanceof UnauthorizedException){
json.put("msg","未授权的操作,请与管理员联系.......");
json.put("code",500);
}else{
json.put("msg","内部错误.......");
json.put("code",500);
}
return json;
}
}
6.测试
把前面的标签注释,然后登录zhangsan,点击加了权限的方法