shiro:hasPermission 隐藏页面无权访问的资源,因为层层调用方法,下面可能有点儿乱,注意上下结合看方法名。
jsp:
organ_list.jsp
<shiro:hasPermission name="jigouxinxi-xinzeng">
<button class="layui-btn layui-btn-primary" onclick="addOrgan()">
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>新增</button>
</shiro:hasPermission>
jsp引擎将其转成Java文件,jvm编译成.class文件
java
organ_001alist_jsp.java
补充:jsp解析
jsp的文件名中有下划线,将下划线替换为下划线加上一个三位数的数字和一个字母比如:_001a,将.jsp替换成_jsp,后面加上后缀名.java。
将替换后的文件名作为类名,继承org.apache.jasper.runtime.HttpJspBase,实现org.apache.jasper.runtime.JspSourceDependent
重写HttpJspBase类中的
void _jspInit()
void _jspService(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)
void _jspDestroy()
Map<java.lang.String,java.lang.Long> getDependants()
引用的标签函数将被解析成Java方法,在_jspService方法里调用。
// 1. 声明
private org.apache.jasper.runtime.TagHandlerPool _005fjspx_005ftagPool_005fshiro_005fhasPermission_0026_005fname;
public void _jspInit() {
// 2. 实例化
_005fjspx_005ftagPool_005fshiro_005fhasPermission_0026_005fname = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool(getServletConfig());
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html; charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
out = pageContext.getOut();
_jspx_out = out;
/* 省略 */
out.write("...");
// 3. 调用解析后的标签方法
if (_jspx_meth_shiro_001ahasPermission_001a0(_jspx_page_context))
return;
/* 省略 */
out.write("...");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
private boolean _jspx_meth_shiro_001ahasPermission_001a0(javax.servlet.jsp.PageContext _jspx_page_context)
throws java.lang.Throwable {
javax.servlet.jsp.PageContext pageContext = _jspx_page_context;
javax.servlet.jsp.JspWriter out = _jspx_page_context.getOut();
// 4. shiro:hasPermission(强转)
org.apache.shiro.web.tags.HasPermissionTag _jspx_th_shiro_001ahasPermission_001a0 = (org.apache.shiro.web.tags.HasPermissionTag) _001ajspx_001atagPool_001ashiro_001ahasPermission_0026_001aname.get(org.apache.shiro.web.tags.HasPermissionTag.class);
_jspx_th_shiro_001ahasPermission_001a0.setPageContext(_jspx_page_context);
_jspx_th_shiro_001ahasPermission_001a0.setParent(null);
// /WEB-INF/view/blc_organ_list.jsp(28,12) name = name type = null reqTime = true required = true fragment = false deferredValue = false expectedTypeName = null deferredMethod = false methodSignature = null
// 5. 赋值shiro 权限标识"jigouxinxi-xinzeng"
_jspx_th_shiro_001ahasPermission_001a0.setName("jigouxinxi-xinzeng");
// 6. 调用doStartTag() 下面详细说做了什么。
int _jspx_eval_shiro_001ahasPermission_001a0 = _jspx_th_shiro_001ahasPermission_001a0.doStartTag();
if (_jspx_eval_shiro_001ahasPermission_001a0 != javax.servlet.jsp.tagext.Tag.SKIP_BODY) {
do {
out.write("\n");
out.write("\t\t\t\t <button class=\"layui-btn layui-btn-primary\" onclick=\"addOrgan()\">\n");
out.write("\t\t\t\t <i class=\"fa fa-plus fa-fw\" aria-hidden=\"true\"></i>新增</button>\n");
out.write("\t\t\t\t ");
int evalDoAfterBody = _jspx_th_shiro_001ahasPermission_001a0.doAfterBody();
if (evalDoAfterBody != javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_AGAIN)
break;
} while (true);
}
if (_jspx_th_shiro_001ahasPermission_001a0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
_001ajspx_001atagPool_001ashiro_001ahasPermission_0026_001aname.reuse(_jspx_th_shiro_001ahasPermission_001a0);
return true;
}
_001ajspx_001atagPool_001ashiro_001ahasPermission_0026_001aname.reuse(_jspx_th_shiro_001ahasPermission_001a0);
return false;
}
核心:
doStartTag()
public int doStartTag() throws JspException {
// 判断shiro 权限标识"jigouxinxi-xinzeng"是否为null
verifyAttributes();
return onDoStartTag();
}
PermissionTag.java
private String name = null;
public PermissionTag() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected void verifyAttributes() throws JspException {
String permission = getName();
if (permission == null || permission.length() == 0) {
String msg = "The 'name' tag attribute must be set.";
throw new JspException(msg);
}
}
public int onDoStartTag() throws JspException {
String p = getName();
boolean show = showTagBody(p);
if (show) {
return TagSupport.EVAL_BODY_INCLUDE;
} else {
return TagSupport.SKIP_BODY;
}
}
复杂的调用过程:从上面【boolean show = showTagBody(p);】开始
org.apache.shiro.web.tags.HasPermissionTag extends PermissionTag
boolean showTagBody(String p){
return isPermitted(p);
}
org.apache.shiro.web.tags.PermissionTag extends SecureTag
boolean isPermitted(String p){
// 调用父类SecureTag里的 Subject getSubject(){ return SecurityUtils.getSubject(); }
return getSubject() != null && getSubject().isPermitted(p);
}
org.apache.shiro.subject.support.DelegatingSubject implements Subject
boolean isPermitted(String permission){
// 调用 boolean hasPrincipals()
// securityManager.isPermitted接收了用户可访问的权限标识集合,和页面上的标签的权限标识
// 他们是如何比较的呢?在最后详细展示。
return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
}
protected boolean hasPrincipals() {
return !CollectionUtils.isEmpty(getPrincipals());
}
public PrincipalCollection getPrincipals() {
List<PrincipalCollection> runAsPrincipals = getRunAsPrincipalsStack();
// 你可能会问为什么runAsPrincipals.get(0)?权限标识不是多个吗?
// 看看shiro是怎么存的,就知道他为什么这么取。在下一个代码块说明。
return CollectionUtils.isEmpty(runAsPrincipals) ? this.principals : runAsPrincipals.get(0);
}
private List<PrincipalCollection> getRunAsPrincipalsStack() {
org.apache.shiro.session.Session session = getSession(false);
if (session != null) {
// 从session中取出shiro权限标识
return (List<PrincipalCollection>) session.getAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
}
return null;
}
shiro是这么存权限标识的:
还记得你自定义的Realm类吗?登录时,你根据登录用户查询了他可访问的权限标识list or set
/**
* 授权
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
logger.info("开始授权");
// 1. 获取当前用户对象用户名
AuthUser authUser = (AuthUser) principals.fromRealm(getName()).iterator().next();
String username = authUser.getUsername();
// 2. 查询根据用户名查询 permission
List<String> permissionList = authUserSrv.queryPermissionListByUsername(username);
// 3. 权限串集合在shiro内部进行比较
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 看这里你把值给了这个对象,那么他又做了什么呢?看下面的代码块。
info.addStringPermissions(permissionList);
return info;
}
经过层层调用调用了下面的方法:(SimpleAuthorizationInfo 对象把权限标识集合给了PrincipalCollection 对象)
private void pushIdentity(PrincipalCollection principals) throws NullPointerException {
if (CollectionUtils.isEmpty(principals)) {
String msg = "Specified Subject principals cannot be null or empty for 'run as' functionality.";
throw new NullPointerException(msg);
}
List<PrincipalCollection> stack = getRunAsPrincipalsStack();
if (stack == null) {
stack = new CopyOnWriteArrayList<PrincipalCollection>();
}
// 看这里
stack.add(0, principals);
Session session = getSession();
session.setAttribute(RUN_AS_PRINCIPALS_SESSION_KEY, stack);
}
private List<PrincipalCollection> getRunAsPrincipalsStack() {
Session session = getSession(false);
if (session != null) {
return (List<PrincipalCollection>) session.getAttribute(RUN_AS_PRINCIPALS_SESSION_KEY);
}
return null;
}
比较 securityManager.isPermitted(PrincipalCollection principals, Permission permission)
org.apache.shiro.realm.AuthorizingRealm
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
AuthorizationInfo info = getAuthorizationInfo(principals);
return isPermitted(permission, info);
}
//changed visibility from private to protected for SHIRO-332
protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
Collection<Permission> perms = getPermissions(info);
if (perms != null && !perms.isEmpty()) {
for (Permission perm : perms) {
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}
org.apache.shiro.authz.permission.WildcardPermission implements Permission
// 更多详情请看从源码中找这个类。
public boolean implies(Permission p) {
// By default only supports comparisons with other WildcardPermissions
if (!(p instanceof WildcardPermission)) {
return false;
}
WildcardPermission wp = (WildcardPermission) p;
List<Set<String>> otherParts = wp.getParts();
int i = 0;
for (Set<String> otherPart : otherParts) {
// If this permission has less parts than the other permission, everything after the number of parts contained
// in this permission is automatically implied, so return true
if (getParts().size() - 1 < i) {
return true;
} else {
Set<String> part = getParts().get(i);
if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
return false;
}
i++;
}
}
扩展:
关于自定义标签的参考文章