zoukankan      html  css  js  c++  java
  • Shiro-授权

    把 realms 配置给SecurityManager

    在认证的时候单个realm是这样配置的:

      <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="cacheManager" ref="cacheManager"/>
            <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
            <!-- 配置session的管理方式 -->
            <!-- <property name="sessionMode" value="native"/> -->
            <property name="realm" ref="jdbcRealm"/>
            </property>
        </bean>

     多个realm是这样配置的:

      1).将多个realm配置给 authenticator

    <!-- 配置多个Realm -->
        <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
            <property name="realms">
                <list>
                    <ref bean="jdbcRealm"/>
                    <ref bean="secondRealm"/>
                </list>
            </property>
            <property name="authenticationStrategy">
                <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
            </property>
        </bean>

      2).将 authenticator 配置给 SecurityManager

     <!-- 
            1.配置SecurityManager! 
        -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="cacheManager" ref="cacheManager"/>
            <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
            <!-- 配置session的管理方式 -->
            <!-- <property name="sessionMode" value="native"/> -->
            <!-- <property name="realm" ref="jdbcRealm"/> -->
            <!-- 配置多个Realm -->
            <property name="authenticator" ref="authenticator"></property>
        </bean>

       其实SecurityManager 中有一个 realms属性

    <!-- 
            1.配置SecurityManager! 
        -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="cacheManager" ref="cacheManager"/>
            <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
            <!-- 配置session的管理方式 -->
            <!-- <property name="sessionMode" value="native"/> -->
            <!-- <property name="realm" ref="jdbcRealm"/> -->
            <!-- 配置多个Realm -->
            <property name="authenticator" ref="authenticator"></property>
            
            <property name="realms"></property>
        </bean>

       那么直接在SecurityManager中配置 realms中是否可以呢,答案是可以的。

     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="cacheManager" ref="cacheManager"/>
            <!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
            <!-- 配置session的管理方式 -->
            <!-- <property name="sessionMode" value="native"/> -->
            <!-- <property name="realm" ref="jdbcRealm"/> -->
            <!-- 配置多个Realm -->
            <property name="authenticator" ref="authenticator"></property>
            
            <property name="realms">
                <list>
                    <ref bean="jdbcRealm"/>
                    <ref bean="secondRealm"/>
                </list>
            </property>
        </bean>
        
        <!-- 配置多个Realm -->
        <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
            <property name="authenticationStrategy">
                <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
            </property>
        </bean>

      在授权中是需要将realms改为这样配置的   

      可以再 ModularRealmAuthenticator 的setRealms 中打个断点

      

      代码往前翻可以看到这里做了强制类型转换

     授权:

      

     默认拦截器:

    •   Shiro内置了很多默认的拦截器,比如身份验证,授权等相关的。默认拦截器可以参考
      org.apache.shiro.web.filter.mgt.DefaultFilter
      public enum DefaultFilter {
      
          anon(AnonymousFilter.class),
          authc(FormAuthenticationFilter.class),
          authcBasic(BasicHttpAuthenticationFilter.class),
          logout(LogoutFilter.class),
          noSessionCreation(NoSessionCreationFilter.class),
          perms(PermissionsAuthorizationFilter.class),
          port(PortFilter.class),
          rest(HttpMethodPermissionFilter.class),
          roles(RolesAuthorizationFilter.class),
          ssl(SslFilter.class),
          user(UserFilter.class);
      
          private final Class<? extends Filter> filterClass;
      
          private DefaultFilter(Class<? extends Filter> filterClass) {
              this.filterClass = filterClass;
          }
      
          public Filter newInstance() {
              return (Filter) ClassUtils.newInstance(this.filterClass);
          }
      
          public Class<? extends Filter> getFilterClass() {
              return this.filterClass;
          }
      
          public static Map<String, Filter> createInstanceMap(FilterConfig config) {
              Map<String, Filter> filters = new LinkedHashMap<String, Filter>(values().length);
              for (DefaultFilter defaultFilter : values()) {
                  Filter filter = defaultFilter.newInstance();
                  if (config != null) {
                      try {
                          filter.init(config);
                      } catch (ServletException e) {
                          String msg = "Unable to correctly init default filter instance of type " +
                                  filter.getClass().getName();
                          throw new IllegalStateException(msg, e);
                      }
                  }
                  filters.put(defaultFilter.name(), filter);
              }
              return filters;
          }
      }

      身份验证相关的有:

       授权相关的:

        

       其他:

      

     为访问路径配置权限:

      1). 修改页面为 list.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
        <h4>List Page</h4>
        
        <a href="admin.jsp">Admin Page</a>
        <br><br>
        
        <a href="user.jsp">User Page</a>
        <br><br>
        
        <a href="shiro/logout">Logout</a>
    </body>
    </html>

      2). 在配置文件的过滤器中添加

      

    <property name="filterChainDefinitions">
                <value>
                    /login.jsp= anon
                    /shiro/login= anon
                    /shiro/logout = logout
                    
                    /user.jsp = roles[user]
                    /admin.jsp = roles[admin]
                    
                    # everything else requires authentication:
                    
                    /** = authc
                </value>
            </property>

      3).授权需要继承 AuthorizingRealm 类,并实现其 doGetAuthorizationInfo 方法

       AuthorizingRealm 类继承自 AuthorizingRealm , 但没有实现 AuthorizingRealm 中的 doGetAuthenticationInfo,

        所以认证和授权 只需要继承 AuthorizingRealm就可以了,同时实现他们两个抽象方法。

      

    package com.java.shiro.realms;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    public class TestRealm extends AuthorizingRealm {
    
        //授权需要实现的方法
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            // TODO Auto-generated method stub
            return null;
        }
    
        //认证需要实现的方法
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            // TODO Auto-generated method stub
            return null;
        }
    
    }

      查看源码得到,多Realm授权中只要有一个通过就可以 ModularRealmAuthorizer

    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
            assertRealmsConfigured();
            for (Realm realm : getRealms()) {
                if (!(realm instanceof Authorizer)) continue;
                if (((Authorizer) realm).hasRole(principals, roleIdentifier)) {
                    return true;
                }
            }
            return false;
        }

      授权Realm的实现

        1). 在原先的ShiroRealm的基础上修改,修改其继承

    package com.java.shiro.realms;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.LockedAccountException;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    public class ShiroRealm extends AuthorizingRealm {
    
        /**
         * 用于认证的Realm保持不变
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            System.out.println("[FirstRealm] doGetAuthenticationInfo " + token);
    
            // 1. 把AuthenticationToken 转换为UsernamePasswordToken
            UsernamePasswordToken up = (UsernamePasswordToken) token;
            // 2. 从UsernamePasswordToken 中来获取username
            String username = up.getUsername();
            // 3. 调用数据库的方法,从数据库中查询username对应的用户记录
            System.out.println("从数据库中获取userName :" + username + " 所对应的用户信息.");
            // 4. 若用户不存在,则可以抛出 UnknownAccoountException 异常
            if ("unknown".equals(username)) {
                throw new UnknownAccountException("用户不存在");
            }
            // 5. 根据用户信息的情况,决定是否需要抛出其他的AuthencationException 异常 假设用户被锁定
            if ("monster".equals(username)) {
                throw new LockedAccountException("用户被锁定");
            }
            // 6. 根据用户的情况,来构建AuthenticationInfo 对象并返回,通常使用的是
            // SimpleAuthenticationInfo
            // 以下信息是从数据库获取的.
    
            Object principal = username; // principal 认证的实体信息.
                                            // 可以是username,也可以是数据表对应的用户的实体类对象
                                            // String credentials =
                                            // "fc1709d0a95a6be30bc5926fdb7f22f4";
                                            // // credentials:密码
            String credentials = null; // credentials:密码
            String realmName = getName();
            AuthenticationInfo info = null;/*
                                             * new
                                             * SimpleAuthenticationInfo(principal,
                                             * credentials, realmName);
                                             */
    
            if ("admin".equals(username)) {
                credentials = "038bdaf98f2037b31f1e75b5b4c9b26e";
            } else if ("user".equals(username)) {
                credentials = "098d2c478e9c11555ce2823231e02ec1";
            }
    
            ByteSource credentialsSalt = ByteSource.Util.bytes(username);// 这里的参数要给个唯一的;
    
            info = new SimpleAuthenticationInfo(principal, credentials,
                    credentialsSalt, realmName);
    
            return info;
        }
    
        public static void main(String[] args) {
            String hashAlgorithmName = "MD5";
            String credentials = "123456";
            int hashIterations = 1024;
            ByteSource credentialsSalt = ByteSource.Util.bytes("user");
            Object obj = new SimpleHash(hashAlgorithmName, credentials,
                    credentialsSalt, hashIterations);
            System.out.println(obj);
        }
    
        /**
         * 这个是用于授权的Realm
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            // 1. 从 PrincipalCollection 中获取来获取登录用户的信息
            //由于我们配置了多个Realm 一个返回的是 seconde,一个返回的是认证实体,这两个Realm在配置认证的时候是有顺序地
            /**
             * <property name="realms">
             *    <list>
             *        <ref bean="jdbcRealm"/>
             *        <ref bean="secondRealm"/>
             *    </list>
             *    </property>
             *
             * 当我们在获取Principal的时候也是有顺序的
             */
            Object principal = principals.getPrimaryPrincipal();
    
            // 2. 利用登录用户的信息来判断当前用户的角色或权限(可能需要查询数据库)
            Set<String> roles = new HashSet<>();
            roles.add("user"); // 这里无论登录的是user 还是 admin 都存放一个user角色
            if ("admin".equals(principal)) {
                roles.add("admin"); // 如果是admin 授权一个admin角色
            }
            AuthorizationInfo info = new SimpleAuthorizationInfo(roles);
            return info;
        }
    }

       

      

      由于我们配置了多个Realm 一个返回的是 seconde,一个返回的是认证实体,这两个Realm在配置认证的时候是有顺序地

        <property name="realms">
                <list>
                    <ref bean="jdbcRealm"/>
                    <ref bean="secondRealm"/>
                </list>
            </property>

      当我们在获取Principal的时候也是有顺序的

      通过查看源代码

    public Object getPrimaryPrincipal() {
            if (isEmpty()) {
                return null;
            }
            return iterator().next();
        }
    public Iterator iterator() {
            return asSet().iterator();
        }
     1 public Set asSet() {
     2         if (realmPrincipals == null || realmPrincipals.isEmpty()) {
     3             return Collections.EMPTY_SET;
     4         }
     5         Set aggregated = new LinkedHashSet();
     6         Collection<Set> values = realmPrincipals.values();
     7         for (Set set : values) {
     8             aggregated.addAll(set);
     9         }
    10         if (aggregated.isEmpty()) {
    11             return Collections.EMPTY_SET;
    12         }
    13         return Collections.unmodifiableSet(aggregated);
    14     }

      上述代码第6行得到  realmPrincipals 的类型 LinkedHashMap,只有这样才能保证我得到的是希望的那个值。

     启动Tomcat

    使用admin登录 可以访问两个页面,user登录只能访问一个页面

    • Permissions

      

     

    • 授权流程

      

       

      

  • 相关阅读:
    码农自白:这样成为谷歌工程师
    Vim命令合集
    应该知道的Linux技巧
    在Ubuntu上建立Arm Linux 开发环境
    Linux 下socket通信终极指南(附TCP、UDP完整代码)
    Socket通信原理和实践
    用 gdb 调试 GCC 程序
    Quartz学习记录
    shiro学习记录(三)
    shiro学习记录(二)
  • 原文地址:https://www.cnblogs.com/wq3435/p/6267276.html
Copyright © 2011-2022 走看看