Shiro 安全框架(三)
前面介绍了自定义 Realm 的两个方法的作用和简单写法。
这里稍微详细介绍授权方法联动前端页面。
前端页面
采用的是 JSP,需要引入 shiro 标签。
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<base href="<%=basePath%>"/>
<title>Document</title>
</head>
<body>
<h1>系统主页v1.0</h1>
<a href="user/logout">用户退出</a>
<ul>
<shiro:hasAnyRoles name="user,root">
<li><a href="">用户管理</a></li>
</shiro:hasAnyRoles>
<shiro:hasRole name="root">
<li><a href="">商品管理</a></li>
<li><a href="">订单管理</a></li>
<li><a href="">物流管理</a></li>
</shiro:hasRole>
</ul>
</body>
</html>
自定义 Realm
public class CustomerRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
if (primaryPrincipal.equals("root")){ // 假数据,一般根据数据库中登录的用户分配对应的权限
// 添加授权信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 设置 info 的授权,通过 addRole 实现给当前登录的主体授予角色
info.addRole("user");
// info.addRoles(new ArrayList<String>());
return info;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证触发");
String principal = (String) authenticationToken.getPrincipal();
// 默认是类名首字母小写,也可以指定name
//UserService userService = (UserService) SpringBeanFactoryUtils.getBean("userService");
UserService service = SpringBeanFactoryUtils.getBean(UserService.class);
User user = service.findUserByName(principal);
// 从数据库查询用户进行认证
if (!ObjectUtils.isEmpty(user)){
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),ByteSource.Util.bytes(user.getSalt()),this.getName());
}
return null;
}
}
登录 root 用户(具有 root 授权)查看:
登录 tom 用户(具有 user 授权)查看:
实现了前端页面与后端实际用户授权进行联动,在前端完成权限控制。
除了角色(role),还有对应资源的控制(permission),info.addStringPermission("资源标识符:操作:资源实例标识符")。进而对特定角色的指定资源也进行权限控制。
下面就介绍在代码中完成权限控制,这个权限控制应该发生在 Controller 层,对于个别需要权限判断的请求,可以对其进行权限判断,来做出相应的处理:
这里就用之前第二篇的代码演示:
// 认证通过
if (subject.isAuthenticated()){
// 1. 基于角色权限控制
System.out.println(subject.hasRole("admin")); // 查询主体是否有 admin 这个角色
// 2. 多角色权限控制
boolean b = subject.hasAllRoles(Arrays.asList("admin","user")); // 查询主体是否有 admin、user 这两个角色,如果没有返回false
System.out.println(b);
// 3. 查看主体分别有那些角色
boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "super", "user"));
for (boolean aBoolean : booleans) {
System.out.println(aBoolean);
}
System.out.println("================基于权限字符串的访问控制================");
// 4. 基于权限字符串的访问控制 资源标识符:操作:资源类型
// 查看主体是否具有 对 admin 下的 001 资源具有 create 操作的权限
System.out.println(subject.isPermitted("admin:create:001"));
// 查询主体分别对 "admin:create:*"、"user:*:001" 是否具有权限
boolean[] permitted = subject.isPermitted("admin:create:*", "user:*:001","user:create:002");
for (boolean b1 : permitted) {
System.out.println(b1);
}
// 查看主体是否同时具有权限
System.out.println(subject.isPermittedAll("admin:create:*", "user:update:001","user:create:002"));
}
shiro 权限控制有三种,剩下的一种就是走注解方式:
@RequiresRoles(value={"user","admin","root"}) // 用于判断角色,参数是 String[],如果只有一个角色可以不加 value 直接写字符串,如果主体没有对应的 role 则报错,反之可以调用该方法。(需要注意的是如果多个 role 必须是同时具有上述的角色)
@RequiresPermissions(value={"user:update:01","admin*","root:*:02"}) // 用来判断权限字符串