• 关于Apache Shiro权限框架的一些使用误区的解释


       多了不说了,进入正题,shiro是个权限框架提供权限管理等功能,网上的教程一般都是互相抄,比如<shiro:principal property="xxx"/>这个标签,网上教程告诉你可以用来获取登录用户的任何属性,但现实中如果你这么写,并且按照开涛教程上写的登陆逻辑,肯定百分百报错,这是为什么呢?因为网上教程的登录部分一般这么写:

      这是重写authorizingrealm的dogetAuthenticationinfo方法:

    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    // TODO Auto-generated method stub
    String username=(String) token.getPrincipal();
    User user=userService.getUserByUsername(username);
    if(user==null){
    throw new UnknownAccountException();
    }
    if(user.getUserState()==4){
    throw new LockedAccountException();
    }
    SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(
    user.getUsername(),
    user.getPassword(),
    ByteSource.Util.bytes(user.getCredentialsSalt()),
    this.getName()
    );
     
    return info;


    网上很多教程都是这么教给大家的, 然后在controller里用:
    UsernameAndPasswordToken token=new UsernameAndPasswordToken(username,password);
    subject.login(token);
    ....
    这一系列逻辑来进行登陆,登陆成功后在页面通过<shiro:principal property="username"/>来获取登录用户属性,然后你这么做了就报错了。
    为什么?来看源码,源码一是一手鞋,官方文档是二手鞋,网上教程是三手鞋,鞋都穿三手了,兴许会有脚气。。。

        public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {
            this.principals = new SimplePrincipalCollection(principal, realmName);
            this.credentials = hashedCredentials;
            this.credentialsSalt = credentialsSalt;

        }
    上面是SimpleAuthenticationInfo源码的一个构造方法,这里第一个参数就是你刚才传入的用户名,第二个参数就是你传入的密码,但是方法定义中这两个参数都是Object类型,尤其是第一个principal参数,它的意义远远不止用户名那么简单,它是用户的所有认证信息集合,登陆成功后,<shiro:principal/>标签一旦有property属性,PrincipalTag类也就是标签的支持类,会从Subject的principalcollection里将principal取出,取出的就是你传入的第一个参数,如果你传了个string类型的用户名,那么你只能获取用户名。
    仔细看那个this.principals=new SimplePrincipalCollection,这一行,这一行构造了一个新的对象并将引用给了principals,而principals就是principalcollection。
    再来看Principal那个标签<shiro:principal property=""/>那个,打开PrincipalTag类,看到onDoStartTag方法:

        public int onDoStartTag() throws JspException {
            String strValue = null;
     
            if (getSubject() != null) {
     
                // Get the principal to print out
                Object principal;
     
                if (type == null) {
                    principal = getSubject().getPrincipal();
                } else {
                    principal = getPrincipalFromClassName();
                }
     
                // Get the string value of the principal
                if (principal != null) {
                    if (property == null) {
                        strValue = principal.toString();
                    } else {
                        strValue = getPrincipalProperty(principal, property);
                    }
                }
     
            }
     
            // Print out the principal value if not null
            if (strValue != null) {
                try {
                    pageContext.getOut().write(strValue);
                } catch (IOException e) {
                    throw new JspTagException("Error writing [" + strValue + "] to JSP.", e);
                }
            }
     
            return SKIP_BODY;

        } 
    看到那个Object principal这个方法变量了么?如果标签里没有type属性,那么就直接调用getPrincipal方法,再打开这个方法,看到subject里是这么写的:

        public Object getPrincipal() {
            return getPrimaryPrincipal(getPrincipals());

        } 
    getPrincipals是获取principalcollection,getprimaryprincipal就是获取这个principalcollection的第一个元素,用的是迭代器的方式获取。具体的我不列出来了,请参见SimplePrincipalcollection的源代码。
    第一个元素你最初放的是用户名,所以你可以取得这个用户名。 如果type不为空,就会去principalcollection中找和这个type类型一致的一个对象,看源码:

        private Object getPrincipalFromClassName() {
            Object principal = null;
     
            try {
                Class cls = Class.forName(type);
                principal = getSubject().getPrincipals().oneByType(cls);
            } catch (ClassNotFoundException e) {
                if (log.isErrorEnabled()) {
                    log.error("Unable to find class for name [" + type + "]");
                }
            }
            return principal;

        } 
    那个oneByType方法就是在principalcollection中找和type属性一致的那个类的对象,将其作为principal,如果<shiro:property/>标签有了property属性,然后用java内省机制获取bean的属性,参见getPrincipalProperty方法,因为方法体太长我就不列出了。 
    所以你现在知道该怎么做了吧?
    在你的realm里这么写:

    List<Object> principals=new ArrayList<Object>();
    principals.add(user.getUsername());
    principals.add(user);
    SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(
    principals,
    user.getPassword(),
    ByteSource.Util.bytes(user.getCredentialsSalt()),
    this.getName()

    );
    构建一个list,第一个元素是用户名,第二个元素是user对象。第一个元素用来登陆,第二个元素用来获取登陆后user对象的属性。
    他们到底怎么判断登陆的呢?
    先参见HashedCredentialsMatcher类的这个方法,这是用来判断是否可以登录成功的方法:  
      public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

            Object tokenHashedCredentials = hashProvidedCredentials(token, info);
            Object accountCredentials = getCredentials(info);
            return equals(tokenHashedCredentials, accountCredentials);

        } 
    其中第一个参数token就是controller里封装的token,第二个参数info就是你刚才的simpleauthenticationinfo,因为源码层次比较深所以简单点说,这个方法就是再判断两个密码是否相等,因为两个用户名肯定是相等的info的用户名是token传过来的,所以只对比密码就可以了。 
    就写这么多吧,希望大家看了能有启发。 

       不是我写的,是我在开发中遇到问题,特此复制别人解决办法的博客  附上 大牛的链接  http://blog.csdn.net/ccdust/article/details/52300287
  • 相关阅读:
    java容器01--初遇
    java虚拟机(1)--运行时数据区
    java虚拟机(2)--垃圾收集
    java虚拟机(3)--内存分配与回收策略
    java虚拟机(4)--类加载机制
    bash编程的信号捕获:
    awk纯干货
    shell中各种括号的作用()、(())、[]、[[]]、{}
    find
    awk
  • 原文地址:https://www.cnblogs.com/java-synchronized/p/7471306.html
Copyright © 2020-2023  润新知