【Shiro原理一】shiro:hasPermission 隐藏页面无权访问的资源

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++;
            }
        }

扩展:
关于自定义标签的参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值