zoukankan      html  css  js  c++  java
  • SpringMVC整合Shiro

    摘要: SpringMVC整合Shiro,Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能。


    第一步:配置web.xml

    <!-- 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 -->  
    <!-- 这里filter-name必须对应applicationContext.xml中定义的<bean id="shiroFilter"/> -->  
    <!-- 使用[/*]匹配所有请求,保证所有的可控请求都经过Shiro的过滤 -->  
    <!-- 通常会将此filter-mapping放置到最前面(即其他filter-mapping前面),以保证它是过滤器链中第一个起作用的 -->  
    <filter>  
        <filter-name>shiroFilter</filter-name>  
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
        <init-param>  
        <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->  
        <param-name>targetFilterLifecycle</param-name>  
        <param-value>true</param-value>  
        </init-param>  
    </filter>  
    <filter-mapping>  
            <filter-name>shiroFilter</filter-name>  
            <url-pattern>/*</url-pattern>  
    </filter-mapping>

    第二步:配置applicationContext.xml

    <!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的ShiroDbRealm.java -->  
    <bean id="myRealm" class="com.jadyer.realm.MyRealm"/>  
      
    <!-- Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session -->  
    <!-- 即<property name="sessionMode" value="native"/>,详细说明见官方文档 -->  
    <!-- 这里主要是设置自定义的单Realm应用,若有多个Realm,可使用'realms'属性代替 -->  
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
        <property name="realm" ref="myRealm"/>  
    </bean>  
      
    <!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 -->  
    <!-- Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 -->  
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
        <!-- Shiro的核心安全接口,这个属性是必须的 -->  
        <property name="securityManager" ref="securityManager"/>  
        <!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->  
        <property name="loginUrl" value="/"/>  
        <!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) -->  
        <!-- <property name="successUrl" value="/system/main"/> -->  
        <!-- 用户访问未对其授权的资源时,所显示的连接 -->  
        <!-- 若想更明显的测试此属性可以修改它的值,如unauthor.jsp,然后用[玄玉]登录后访问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp -->  
        <property name="unauthorizedUrl" value="/"/>  
        <!-- Shiro连接约束配置,即过滤链的定义 -->  
        <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 -->  
        <!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 -->  
        <!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 -->  
        <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter -->  
        <property name="filterChainDefinitions">  
            <value>  
                 /mydemo/login=anon  
                 /mydemo/getVerifyCodeImage=anon  
                 /main**=authc  
                 /user/info**=authc  
                 /admin/listUser**=authc,perms[admin:manage]  
            </value>  
        </property>
    </bean>  
      
    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->  
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
      
    <!-- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->  
    <!-- 配置以下两个bean即可实现此功能 -->  
    <!-- Enable Shiro Annotations for Spring-configured beans. Only run after the lifecycleBeanProcessor has run -->  
    <!-- 由于本例中并未使用Shiro注解,故注释掉这两个bean(个人觉得将权限通过注解的方式硬编码在程序中,查看起来不是很方便,没必要使用) -->  
    <!--   
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
      <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
        <property name="securityManager" ref="securityManager"/>  
      </bean>  
    -->


    第三步:自定义的Realm类

    public class MyRealm extends AuthorizingRealm {  
        /** 
         * 为当前登录的Subject授予角色和权限 
         * @see  经测试:本例中该方法的调用时机为需授权资源被访问时 
         * @see  经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache 
         * @see  个人感觉若使用了Spring3.1开始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache 
         * @see  比如说这里从数据库获取权限信息时,先去访问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache 
         */  
        @Override  
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){  
            //获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()  
            String currentUsername = (String)super.getAvailablePrincipal(principals);  
    //      List<String> roleList = new ArrayList<String>();  
    //      List<String> permissionList = new ArrayList<String>();  
    //      //从数据库中获取当前登录用户的详细信息  
    //      User user = userService.getByUsername(currentUsername);  
    //      if(null != user){  
    //          //实体类User中包含有用户角色的实体类信息  
    //          if(null!=user.getRoles() && user.getRoles().size()>0){  
    //              //获取当前登录用户的角色  
    //              for(Role role : user.getRoles()){  
    //                  roleList.add(role.getName());  
    //                  //实体类Role中包含有角色权限的实体类信息  
    //                  if(null!=role.getPermissions() && role.getPermissions().size()>0){  
    //                      //获取权限  
    //                      for(Permission pmss : role.getPermissions()){  
    //                          if(!StringUtils.isEmpty(pmss.getPermission())){  
    //                              permissionList.add(pmss.getPermission());  
    //                          }  
    //                      }  
    //                  }  
    //              }  
    //          }  
    //      }else{  
    //          throw new AuthorizationException();  
    //      }  
    //      //为当前用户设置角色和权限  
    //      SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();  
    //      simpleAuthorInfo.addRoles(roleList);  
    //      simpleAuthorInfo.addStringPermissions(permissionList);  
            SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();  
            //实际中可能会像上面注释的那样从数据库取得  
            if(null!=currentUsername && "mike".equals(currentUsername)){  
                //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色    
                simpleAuthorInfo.addRole("admin");  
                //添加权限  
                simpleAuthorInfo.addStringPermission("admin:manage");  
                System.out.println("已为用户[mike]赋予了[admin]角色和[admin:manage]权限");  
                return simpleAuthorInfo;  
            }
            //若该方法什么都不做直接返回null的话,就会导致任何用户访问/admin/listUser.jsp时都会自动跳转到unauthorizedUrl指定的地址  
            //详见applicationContext.xml中的<bean id="shiroFilter">的配置  
            return null;  
        }  
      
          
        /** 
         * 验证当前登录的Subject 
         * @see  经测试:本例中该方法的调用时机为LoginController.login()方法中执行Subject.login()时 
         */  
        @Override  
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  
            //获取基于用户名和密码的令牌  
            //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的  
            //两个token的引用都是一样的
            UsernamePasswordToken token = (UsernamePasswordToken)authcToken;  
            System.out.println("验证当前Subject时获取到token为" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  
    //      User user = userService.getByUsername(token.getUsername());  
    //      if(null != user){  
    //          AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), user.getNickname());  
    //          this.setSession("currentUser", user);  
    //          return authcInfo;  
    //      }else{  
    //          return null;  
    //      }  
            //此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息  
            //说白了就是第一个参数填登录用户名,第二个参数填合法的登录密码(可以是从数据库中取到的,本例中为了演示就硬编码了)  
            //这样一来,在随后的登录页面上就只有这里指定的用户和密码才能通过验证  
            if("mike".equals(token.getUsername())){  
                AuthenticationInfo authcInfo = new SimpleAuthenticationInfo("mike", "mike", this.getName());  
                this.setSession("currentUser", "mike");  
                return authcInfo;  
            }
            //没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常  
            return null;  
        }  
          
          
        /** 
         * 将一些数据放到ShiroSession中,以便于其它地方使用 
         * @see  比如Controller,使用时直接用HttpSession.getAttribute(key)就可以取到 
         */  
        private void setSession(Object key, Object value){  
            Subject currentUser = SecurityUtils.getSubject();  
            if(null != currentUser){  
                Session session = currentUser.getSession();  
                System.out.println("Session默认超时时间为[" + session.getTimeout() + "]毫秒");  
                if(null != session){  
                    session.setAttribute(key, value);  
                }  
            }  
        }  
    }


    你可能还需要: freemarker中使用shiro标签


    Velocity模版进行shiro验证


    楼主打包了一份自己写的Shiro验证框架实例,多多支持 亲,点我去下载!

  • 相关阅读:
    保持URL不变和数字验证
    centOS ftp key?
    本地环境测试二级域名
    linux 解决You don't have permission to access 问题
    php smarty section loop
    php header Cannot modify header information headers already sent by ... 解决办法
    linux部分命令
    Linux 里面的文件操作权限说明
    用IT网络和安全专业人士视角来裁剪云的定义
    SQL Server 2008 R2炫酷报表"智"作有方
  • 原文地址:https://www.cnblogs.com/zhousiwei/p/10625889.html
Copyright © 2011-2022 走看看