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

    这里用的是SpringMVC-3.2.4和Shiro-1.2.2,演示样例代码例如以下


    首先是web.xml

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <web-app version="2.5"  
    3.     xmlns="http://java.sun.com/xml/ns/javaee"  
    4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    5.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
    6.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
    7.     <!-- Web容器载入顺序ServletContext--context-param--listener--filter--servlet -->  
    8.   
    9.     <!-- 指定Spring的配置文件 -->  
    10.     <!-- 否则Spring会默认从WEB-INF下寻找配置文件,contextConfigLocation属性是Spring内部固定的 -->  
    11.     <!-- 通过ContextLoaderListener的父类ContextLoader的第120行发现CONFIG_LOCATION_PARAM固定为contextConfigLocation -->  
    12.     <context-param>  
    13.         <param-name>contextConfigLocation</param-name>  
    14.         <param-value>classpath:applicationContext.xml</param-value>  
    15.     </context-param>  
    16.   
    17.     <!-- 防止发生java.beans.Introspector内存泄露,应将它配置在ContextLoaderListener的前面 -->  
    18.     <!-- 具体描写叙述见http://blog.csdn.net/jadyer/article/details/11991457 -->  
    19.     <listener>  
    20.         <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>  
    21.     </listener>  
    22.       
    23.     <!-- 实例化Spring容器 -->  
    24.     <!-- 应用启动时,该监听器被运行,它会读取Spring相关配置文件,其默认会到WEB-INF中查找applicationContext.xml -->  
    25.     <!-- http://starscream.iteye.com/blog/1107036 -->  
    26.     <!-- http://www.davenkin.me/post/2012-10-18/40039948363 -->  
    27.     <!-- WebApplicationContextUtils.getWebApplicationContext() -->  
    28.     <listener>  
    29.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    30.     </listener>  
    31.   
    32.     <!-- 解决乱码问题 -->  
    33.     <!-- forceEncoding默觉得false,此时效果可大致理解为request.setCharacterEncoding("UTF-8") -->  
    34.     <!-- forceEncoding=true后,可大致理解为request.setCharacterEncoding("UTF-8")和response.setCharacterEncoding("UTF-8") -->  
    35.     <filter>  
    36.         <filter-name>SpringEncodingFilter</filter-name>  
    37.         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
    38.         <init-param>  
    39.             <param-name>encoding</param-name>  
    40.             <param-value>UTF-8</param-value>  
    41.         </init-param>  
    42.         <init-param>  
    43.             <param-name>forceEncoding</param-name>  
    44.             <param-value>true</param-value>  
    45.         </init-param>  
    46.     </filter>  
    47.     <filter-mapping>  
    48.         <filter-name>SpringEncodingFilter</filter-name>  
    49.         <url-pattern>/*</url-pattern>  
    50.     </filter-mapping>  
    51.       
    52.     <!-- 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 -->  
    53.     <!-- 这里filter-name必须相应applicationContext.xml中定义的<bean id="shiroFilter"/> -->  
    54.     <!-- 使用[/*]匹配全部请求,保证全部的可控请求都经过Shiro的过滤 -->  
    55.     <!-- 一般会将此filter-mapping放置到最前面(即其它filter-mapping前面),以保证它是过滤器链中第一个起作用的 -->  
    56.     <filter>  
    57.         <filter-name>shiroFilter</filter-name>  
    58.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
    59.         <init-param>  
    60.             <!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->  
    61.             <param-name>targetFilterLifecycle</param-name>  
    62.             <param-value>true</param-value>  
    63.         </init-param>  
    64.     </filter>  
    65.     <filter-mapping>  
    66.         <filter-name>shiroFilter</filter-name>  
    67.         <url-pattern>/*</url-pattern>  
    68.     </filter-mapping>  
    69.   
    70.     <!-- SpringMVC核心分发器 -->  
    71.     <servlet>  
    72.         <servlet-name>SpringMVC</servlet-name>  
    73.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    74.         <init-param>  
    75.             <param-name>contextConfigLocation</param-name>  
    76.             <param-value>classpath:applicationContext.xml</param-value>  
    77.         </init-param>  
    78.     </servlet>  
    79.     <servlet-mapping>  
    80.         <servlet-name>SpringMVC</servlet-name>  
    81.         <url-pattern>/</url-pattern>  
    82.     </servlet-mapping>  
    83.   
    84.     <!-- Session超时30分钟(零或负数表示会话永不超时) -->  
    85.     <!--   
    86.     <session-config>  
    87.         <session-timeout>30</session-timeout>  
    88.     </session-config>  
    89.      -->  
    90.   
    91.     <!-- 默认欢迎页 -->  
    92.     <!-- Servlet2.5中可直接在此处运行Servlet应用,如<welcome-file>servlet/InitSystemParamServlet</welcome-file> -->  
    93.     <!-- 这里使用了SpringMVC提供的<mvc:view-controller>标签,实现了首页隐藏的目的,详见applicationContext.xml -->  
    94.     <!--   
    95.     <welcome-file-list>  
    96.         <welcome-file>login.jsp</welcome-file>  
    97.     </welcome-file-list>  
    98.      -->  
    99.       
    100.     <error-page>  
    101.         <error-code>405</error-code>  
    102.         <location>/WEB-INF/405.html</location>  
    103.     </error-page>  
    104.     <error-page>  
    105.         <error-code>404</error-code>  
    106.         <location>/WEB-INF/404.jsp</location>  
    107.     </error-page>  
    108.     <error-page>  
    109.         <error-code>500</error-code>  
    110.         <location>/WEB-INF/500.jsp</location>  
    111.     </error-page>  
    112.     <error-page>  
    113.         <exception-type>java.lang.Throwable</exception-type>  
    114.         <location>/WEB-INF/500.jsp</location>  
    115.     </error-page>  
    116. </web-app>  
    以下是用于显示Request method 'GET' not supported的//WebRoot//WEB-INF//405.html

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
    2. <html>  
    3.     <head>  
    4.         <title>405.html</title>  
    5.         <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
    6.     </head>  
    7.     <body>  
    8.         <font color="blue">  
    9.             Request method 'GET' not supported  
    10.             <br/><br/>  
    11.             The specified HTTP method is not allowed for the requested resource.  
    12.         </font>  
    13.     </body>  
    14. </html>  
    以下是同意匿名用户訪问的//WebRoot//login.jsp

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <%@ page language="java" pageEncoding="UTF-8"%>  
    2.   
    3. <script type="text/javascript">  
    4. <!--  
    5. function reloadVerifyCode(){  
    6.     document.getElementById('verifyCodeImage').setAttribute('src', '${pageContext.request.contextPath}/mydemo/getVerifyCodeImage');  
    7. }  
    8. //-->  
    9. </script>  
    10.   
    11. <div style="color:red; font-size:22px;">${message_login}</div>  
    12.   
    13. <form action="<%=request.getContextPath()%>/mydemo/login" method="POST">  
    14.     姓名:<input type="text" name="username"/><br/>  
    15.     密码:<input type="text" name="password"/><br/>  
    16.     验证:<input type="text" name="verifyCode"/>  
    17.          &nbsp;&nbsp;  
    18.          <img id="verifyCodeImage" onclick="reloadVerifyCode()" src="<%=request.getContextPath()%>/mydemo/getVerifyCodeImage"/><br/>  
    19.     <input type="submit" value="确认"/>  
    20. </form>  
    以下是用户登录后显示的//WebRoot//main.jsp

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <%@ page language="java" pageEncoding="UTF-8"%>  
    2. 普通用户可訪问<a href="<%=request.getContextPath()%>/mydemo/getUserInfo" target="_blank">用户信息页面</a>  
    3. <br/>  
    4. <br/>  
    5. 管理员可訪问<a href="<%=request.getContextPath()%>/admin/listUser.jsp" target="_blank">用户列表页面</a>  
    6. <br/>  
    7. <br/>  
    8. <a href="<%=request.getContextPath()%>/mydemo/logout" target="_blank">Logout</a>  
    以下是仅仅有管理员才同意訪问的//WebRoot//admin//listUser.jsp

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <%@ page language="java" pageEncoding="UTF-8"%>  
    2. This is listUser.jsp  
    3. <br/>  
    4. <br/>  
    5. <a href="<%=request.getContextPath()%>/mydemo/logout" target="_blank">Logout</a>  
    以下是普通的登录用户所同意訪问的//WebRoot//user//info.jsp

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <%@ page language="java" pageEncoding="UTF-8"%>  
    2. 当前登录的用户为${currUser}  
    3. <br/>  
    4. <br/>  
    5. <a href="<%=request.getContextPath()%>/mydemo/logout" target="_blank">Logout</a>  
    以下是//src//log4j.properties

    [ruby] view plain copy
     print?

    在CODE上查看代码片派生到我的代码片

    1. #use Root for GobalConfig  
    2. log4j.rootLogger=DEBUG,CONSOLE  
    3.   
    4. log4j.logger.java.sql=DEBUG  
    5. log4j.logger.org.apache.shiro=DEBUG  
    6. log4j.logger.org.apache.commons=DEBUG  
    7. log4j.logger.org.springframework=DEBUG  
    8.   
    9. #use ConsoleAppender for ConsoleOut  
    10. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender  
    11. log4j.appender.CONSOLE.Threshold=DEBUG  
    12. log4j.appender.CONSOLE.Target=System.out  
    13. log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout  
    14. log4j.appender.CONSOLE.layout.ConversionPattern=[%d{yyyyMMdd HH:mm:ss}][%t][%C{1}.%M]%m%n  
    以下是//src//applicationContext.xml

    [html] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. <?xml version="1.0" encoding="UTF-8"?

      >  

    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    4.     xmlns:mvc="http://www.springframework.org/schema/mvc"  
    5.     xmlns:context="http://www.springframework.org/schema/context"  
    6.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
    7.                         http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
    8.                         http://www.springframework.org/schema/mvc  
    9.                         http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd  
    10.                         http://www.springframework.org/schema/context  
    11.                         http://www.springframework.org/schema/context/spring-context-3.2.xsd">  
    12.     <!-- 它背后注冊了非常多用于解析注解的处理器,当中就包含<context:annotation-config/>配置的注解所使用的处理器 -->  
    13.     <!-- 所以配置了<context:component-scan base-package="">之后,便无需再配置<context:annotation-config> -->  
    14.     <context:component-scan base-package="com.jadyer"/>  
    15.       
    16.     <!-- 启用SpringMVC的注解功能,它会自己主动注冊HandlerMapping、HandlerAdapter、ExceptionResolver的相关实例 -->  
    17.     <mvc:annotation-driven/>  
    18.   
    19.     <!-- 配置SpringMVC的视图解析器 -->  
    20.     <!-- 其viewClass属性的默认值就是org.springframework.web.servlet.view.JstlView -->  
    21.     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
    22.         <property name="prefix" value="/"/>  
    23.         <property name="suffix" value=".jsp"/>  
    24.     </bean>  
    25.   
    26.     <!-- 默认訪问跳转到登录页面(即定义无需Controller的url<->view直接映射) -->  
    27.     <mvc:view-controller path="/" view-name="forward:/login.jsp"/>  
    28.   
    29.     <!-- 因为web.xml中设置是:由SpringMVC拦截全部请求,于是在读取静态资源文件的时候就会受到影响(说白了就是读不到) -->  
    30.     <!-- 经过以下的配置。该标签的作用就是:全部页面中引用"/js/**"的资源,都会从"/resources/js/"里面进行查找 -->  
    31.     <!-- 我们能够訪问http://IP:8080/xxx/js/my.css和http://IP:8080/xxx/resources/js/my.css对照出来 -->  
    32.     <mvc:resources mapping="/js/**" location="/resources/js/"/>  
    33.     <mvc:resources mapping="/css/**" location="/resources/css/"/>  
    34.     <mvc:resources mapping="/WEB-INF/**" location="/WEB-INF/"/>  
    35.   
    36.     <!-- SpringMVC在超出上传文件限制时,会抛出org.springframework.web.multipart.MaxUploadSizeExceededException -->  
    37.     <!-- 该异常是SpringMVC在检查上传的文件信息时抛出来的,并且此时还没有进入到Controller方法中 -->  
    38.     <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
    39.         <property name="exceptionMappings">  
    40.             <props>  
    41.                 <!-- 遇到MaxUploadSizeExceededException异常时,自己主动跳转到/WEB-INF/error_fileupload.jsp页面 -->  
    42.                 <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">WEB-INF/error_fileupload</prop>  
    43.                 <!-- 处理其他异常(包含Controller抛出的) -->  
    44.                 <prop key="java.lang.Throwable">WEB-INF/500</prop>  
    45.             </props>  
    46.         </property>  
    47.     </bean>  
    48.   
    49.     <!-- 继承自AuthorizingRealm的自己定义Realm,即指定Shiro验证用户登录的类为自己定义的ShiroDbRealm.java -->  
    50.     <bean id="myRealm" class="com.jadyer.realm.MyRealm"/>  
    51.   
    52.     <!-- Shiro默认会使用Servlet容器的Session,可通过sessionMode属性来指定使用Shiro原生Session -->  
    53.     <!-- 即<property name="sessionMode" value="native"/>,具体说明见官方文档 -->  
    54.     <!-- 这里主要是设置自己定义的单Realm应用,若有多个Realm,可使用'realms'属性取代 -->  
    55.     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
    56.         <property name="realm" ref="myRealm"/>  
    57.     </bean>  
    58.   
    59.     <!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持不论什么基于URL路径表达式的、自己定义的过滤器的运行 -->  
    60.     <!-- Web应用中,Shiro可控制的Web请求必须经过Shiro主过滤器的拦截,Shiro对基于Spring的Web应用提供了完美的支持 -->  
    61.     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    62.         <!-- Shiro的核心安全接口,这个属性是必须的 -->  
    63.         <property name="securityManager" ref="securityManager"/>  
    64.         <!-- 要求登录时的链接(可依据项目的URL进行替换),非必须的属性,默认会自己主动寻找Webproject根文件夹下的"/login.jsp"页面 -->  
    65.         <property name="loginUrl" value="/"/>  
    66.         <!-- 登录成功后要跳转的连接(本例中此属性用不到,由于登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) -->  
    67.         <!-- <property name="successUrl" value="/system/main"/> -->  
    68.         <!-- 用户訪问未对其授权的资源时,所显示的连接 -->  
    69.         <!-- 若想更明显的測试此属性能够改动它的值,如unauthor.jsp,然后用[玄玉]登录后訪问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp -->  
    70.         <property name="unauthorizedUrl" value="/"/>  
    71.         <!-- Shiro连接约束配置,即过滤链的定义 -->  
    72.         <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 -->  
    73.         <!-- 以下value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 -->  
    74.         <!-- anon:它相应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示參数,例如说login.jsp?main这样的 -->  
    75.         <!-- authc:该过滤器下的页面必须验证后才干訪问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter -->  
    76.         <property name="filterChainDefinitions">  
    77.             <value>  
    78.                 /mydemo/login=anon  
    79.                 /mydemo/getVerifyCodeImage=anon  
    80.                 /main**=authc  
    81.                 /user/info**=authc  
    82.                 /admin/listUser**=authc,perms[admin:manage]  
    83.             </value>  
    84.         </property>  
    85.     </bean>  
    86.   
    87.     <!-- 保证实现了Shiro内部lifecycle函数的bean运行 -->  
    88.     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
    89.   
    90.     <!-- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->  
    91.     <!-- 配置下面两个bean就可以实现此功能 -->  
    92.     <!-- Enable Shiro Annotations for Spring-configured beans. Only run after the lifecycleBeanProcessor has run -->  
    93.     <!-- 因为本例中并未使用Shiro注解,故凝视掉这两个bean(个人认为将权限通过注解的方式硬编码在程序中,查看起来不是非常方便,不是必需使用) -->  
    94.     <!--   
    95.     <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
    96.     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
    97.         <property name="securityManager" ref="securityManager"/>  
    98.     </bean>  
    99.      -->  
    100. </beans>  
    以下是自己定义的Realm类----MyRealm.java

    [java] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. package com.jadyer.realm;  
    2.   
    3. import org.apache.commons.lang3.builder.ReflectionToStringBuilder;  
    4. import org.apache.commons.lang3.builder.ToStringStyle;  
    5. import org.apache.shiro.SecurityUtils;  
    6. import org.apache.shiro.authc.AuthenticationException;  
    7. import org.apache.shiro.authc.AuthenticationInfo;  
    8. import org.apache.shiro.authc.AuthenticationToken;  
    9. import org.apache.shiro.authc.SimpleAuthenticationInfo;  
    10. import org.apache.shiro.authc.UsernamePasswordToken;  
    11. import org.apache.shiro.authz.AuthorizationInfo;  
    12. import org.apache.shiro.authz.SimpleAuthorizationInfo;  
    13. import org.apache.shiro.realm.AuthorizingRealm;  
    14. import org.apache.shiro.session.Session;  
    15. import org.apache.shiro.subject.PrincipalCollection;  
    16. import org.apache.shiro.subject.Subject;  
    17.   
    18. /** 
    19.  * 自己定义的指定Shiro验证用户登录的类 
    20.  * @see 在本例中定义了2个用户:jadyer和玄玉,jadyer具有admin角色和admin:manage权限,玄玉不具有不论什么角色和权限 
    21.  * @create Sep 29, 2013 3:15:31 PM 
    22.  * @author 玄玉<http://blog.csdn.net/jadyer> 
    23.  */  
    24. public class MyRealm extends AuthorizingRealm {  
    25.     /** 
    26.      * 为当前登录的Subject授予角色和权限 
    27.      * @see 经測试:本例中该方法的调用时机为需授权资源被訪问时 
    28.      * @see 经測试:而且每次訪问需授权资源时都会运行该方法中的逻辑,这表明本例中默认并未启用AuthorizationCache 
    29.      * @see 个人感觉若使用了Spring3.1開始提供的ConcurrentMapCache支持,则可灵活决定是否启用AuthorizationCache 
    30.      * @see 比方说这里从数据库获取权限信息时,先去訪问Spring3.1提供的缓存,而不使用Shior提供的AuthorizationCache 
    31.      */  
    32.     @Override  
    33.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){  
    34.         //获取当前登录的username,等价于(String)principals.fromRealm(this.getName()).iterator().next()  
    35.         String currentUsername = (String)super.getAvailablePrincipal(principals);  
    36. //      List<String> roleList = new ArrayList<String>();  
    37. //      List<String> permissionList = new ArrayList<String>();  
    38. //      //从数据库中获取当前登录用户的具体信息  
    39. //      User user = userService.getByUsername(currentUsername);  
    40. //      if(null != user){  
    41. //          //实体类User中包括实用户角色的实体类信息  
    42. //          if(null!=user.getRoles() && user.getRoles().size()>0){  
    43. //              //获取当前登录用户的角色  
    44. //              for(Role role : user.getRoles()){  
    45. //                  roleList.add(role.getName());  
    46. //                  //实体类Role中包括有角色权限的实体类信息  
    47. //                  if(null!=role.getPermissions() && role.getPermissions().size()>0){  
    48. //                      //获取权限  
    49. //                      for(Permission pmss : role.getPermissions()){  
    50. //                          if(!StringUtils.isEmpty(pmss.getPermission())){  
    51. //                              permissionList.add(pmss.getPermission());  
    52. //                          }  
    53. //                      }  
    54. //                  }  
    55. //              }  
    56. //          }  
    57. //      }else{  
    58. //          throw new AuthorizationException();  
    59. //      }  
    60. //      //为当前用户设置角色和权限  
    61. //      SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();  
    62. //      simpleAuthorInfo.addRoles(roleList);  
    63. //      simpleAuthorInfo.addStringPermissions(permissionList);  
    64.         SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();  
    65.         //实际中可能会像上面凝视的那样从数据库取得  
    66.         if(null!=currentUsername && "jadyer".equals(currentUsername)){  
    67.             //加入一个角色,不是配置意义上的加入,而是证明该用户拥有admin角色    
    68.             simpleAuthorInfo.addRole("admin");  
    69.             //加入权限  
    70.             simpleAuthorInfo.addStringPermission("admin:manage");  
    71.             System.out.println("已为用户[jadyer]赋予了[admin]角色和[admin:manage]权限");  
    72.             return simpleAuthorInfo;  
    73.         }else if(null!=currentUsername && "玄玉".equals(currentUsername)){  
    74.             System.out.println("当前用户[玄玉]无授权");  
    75.             return simpleAuthorInfo;  
    76.         }  
    77.         //若该方法什么都不做直接返回null的话,就会导致不论什么用户訪问/admin/listUser.jsp时都会自己主动跳转到unauthorizedUrl指定的地址  
    78.         //详见applicationContext.xml中的<bean id="shiroFilter">的配置  
    79.         return null;  
    80.     }  
    81.   
    82.       
    83.     /** 
    84.      * 验证当前登录的Subject 
    85.      * @see 经測试:本例中该方法的调用时机为LoginController.login()方法中运行Subject.login()时 
    86.      */  
    87.     @Override  
    88.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {  
    89.         //获取基于username和password的令牌  
    90.         //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的  
    91.         //两个token的引用都是一样的,本例中是org.apache.shiro.authc.UsernamePasswordToken@33799a1e  
    92.         UsernamePasswordToken token = (UsernamePasswordToken)authcToken;  
    93.         System.out.println("验证当前Subject时获取到token为" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  
    94. //      User user = userService.getByUsername(token.getUsername());  
    95. //      if(null != user){  
    96. //          AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), user.getNickname());  
    97. //          this.setSession("currentUser", user);  
    98. //          return authcInfo;  
    99. //      }else{  
    100. //          return null;  
    101. //      }  
    102.         //此处无需比对,比对的逻辑Shiro会做,我们仅仅需返回一个和令牌相关的正确的验证信息  
    103.         //说白了就是第一个參数填登录username,第二个參数填合法的登录password(能够是从数据库中取到的,本例中为了演示就硬编码了)  
    104.         //这样一来,在随后的登录页面上就仅仅有这里指定的用户和password才干通过验证  
    105.         if("jadyer".equals(token.getUsername())){  
    106.             AuthenticationInfo authcInfo = new SimpleAuthenticationInfo("jadyer""jadyer"this.getName());  
    107.             this.setSession("currentUser""jadyer");  
    108.             return authcInfo;  
    109.         }else if("玄玉".equals(token.getUsername())){  
    110.             AuthenticationInfo authcInfo = new SimpleAuthenticationInfo("玄玉""xuanyu"this.getName());  
    111.             this.setSession("currentUser""玄玉");  
    112.             return authcInfo;  
    113.         }  
    114.         //没有返回登录username相应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常  
    115.         return null;  
    116.     }  
    117.       
    118.       
    119.     /** 
    120.      * 将一些数据放到ShiroSession中,以便于其他地方使用 
    121.      * @see 比方Controller,使用时直接用HttpSession.getAttribute(key)就能够取到 
    122.      */  
    123.     private void setSession(Object key, Object value){  
    124.         Subject currentUser = SecurityUtils.getSubject();  
    125.         if(null != currentUser){  
    126.             Session session = currentUser.getSession();  
    127.             System.out.println("Session默认超时时间为[" + session.getTimeout() + "]毫秒");  
    128.             if(null != session){  
    129.                 session.setAttribute(key, value);  
    130.             }  
    131.         }  
    132.     }  
    133. }  
    以下是处理用户登录的LoginController.java

    [java] view plain copy
     print?在CODE上查看代码片派生到我的代码片
    1. package com.jadyer.controller;  
    2.   
    3. import java.awt.Color;  
    4. import java.awt.image.BufferedImage;  
    5. import java.io.IOException;  
    6.   
    7. import javax.imageio.ImageIO;  
    8. import javax.servlet.http.HttpServletRequest;  
    9. import javax.servlet.http.HttpServletResponse;  
    10.   
    11. import org.apache.commons.lang3.StringUtils;  
    12. import org.apache.commons.lang3.builder.ReflectionToStringBuilder;  
    13. import org.apache.commons.lang3.builder.ToStringStyle;  
    14. import org.apache.shiro.SecurityUtils;  
    15. import org.apache.shiro.authc.AuthenticationException;  
    16. import org.apache.shiro.authc.ExcessiveAttemptsException;  
    17. import org.apache.shiro.authc.IncorrectCredentialsException;  
    18. import org.apache.shiro.authc.LockedAccountException;  
    19. import org.apache.shiro.authc.UnknownAccountException;  
    20. import org.apache.shiro.authc.UsernamePasswordToken;  
    21. import org.apache.shiro.subject.Subject;  
    22. import org.apache.shiro.web.util.WebUtils;  
    23. import org.springframework.stereotype.Controller;  
    24. import org.springframework.web.bind.annotation.RequestMapping;  
    25. import org.springframework.web.bind.annotation.RequestMethod;  
    26. import org.springframework.web.servlet.view.InternalResourceViewResolver;  
    27.   
    28. import com.jadyer.util.VerifyCodeUtil;  
    29.   
    30. /** 
    31.  * 本例中用到的jar文件例如以下 
    32.  * @see aopalliance.jar 
    33.  * @see commons-lang3-3.1.jar 
    34.  * @see commons-logging-1.1.2.jar 
    35.  * @see log4j-1.2.17.jar 
    36.  * @see shiro-all-1.2.2.jar 
    37.  * @see slf4j-api-1.7.5.jar 
    38.  * @see slf4j-log4j12-1.7.5.jar 
    39.  * @see spring-aop-3.2.4.RELEASE.jar 
    40.  * @see spring-beans-3.2.4.RELEASE.jar 
    41.  * @see spring-context-3.2.4.RELEASE.jar 
    42.  * @see spring-core-3.2.4.RELEASE.jar 
    43.  * @see spring-expression-3.2.4.RELEASE.jar 
    44.  * @see spring-jdbc-3.2.4.RELEASE.jar 
    45.  * @see spring-oxm-3.2.4.RELEASE.jar 
    46.  * @see spring-tx-3.2.4.RELEASE.jar 
    47.  * @see spring-web-3.2.4.RELEASE.jar 
    48.  * @see spring-webmvc-3.2.4.RELEASE.jar 
    49.  * @create Sep 30, 2013 11:10:06 PM 
    50.  * @author 玄玉<http://blog.csdn.net/jadyer> 
    51.  */  
    52. @Controller  
    53. @RequestMapping("mydemo")  
    54. public class LoginController {  
    55.     /** 
    56.      * 获取验证码图片和文本(验证码文本会保存在HttpSession中) 
    57.      */  
    58.     @RequestMapping("/getVerifyCodeImage")  
    59.     public void getVerifyCodeImage(HttpServletRequest request, HttpServletResponse response) throws IOException {  
    60.         //设置页面不缓存  
    61.         response.setHeader("Pragma""no-cache");  
    62.         response.setHeader("Cache-Control""no-cache");  
    63.         response.setDateHeader("Expires"0);  
    64.         String verifyCode = VerifyCodeUtil.generateTextCode(VerifyCodeUtil.TYPE_NUM_ONLY, 4null);  
    65.         //将验证码放到HttpSession里面  
    66.         request.getSession().setAttribute("verifyCode", verifyCode);  
    67.         System.out.println("本次生成的验证码为[" + verifyCode + "],已存放到HttpSession中");  
    68.         //设置输出的内容的类型为JPEG图像  
    69.         response.setContentType("image/jpeg");  
    70.         BufferedImage bufferedImage = VerifyCodeUtil.generateImageCode(verifyCode, 90303true, Color.WHITE, Color.BLACK, null);  
    71.         //写给浏览器  
    72.         ImageIO.write(bufferedImage, "JPEG", response.getOutputStream());  
    73.     }  
    74.       
    75.       
    76.     /** 
    77.      * 用户登录 
    78.      */  
    79.     @RequestMapping(value="/login", method=RequestMethod.POST)  
    80.     public String login(HttpServletRequest request){  
    81.         String resultPageURL = InternalResourceViewResolver.FORWARD_URL_PREFIX + "/";  
    82.         String username = request.getParameter("username");  
    83.         String password = request.getParameter("password");  
    84.         //获取HttpSession中的验证码  
    85.         String verifyCode = (String)request.getSession().getAttribute("verifyCode");  
    86.         //获取用户请求表单中输入的验证码  
    87.         String submitCode = WebUtils.getCleanParam(request, "verifyCode");  
    88.         System.out.println("用户[" + username + "]登录时输入的验证码为[" + submitCode + "],HttpSession中的验证码为[" + verifyCode + "]");  
    89.         if (StringUtils.isEmpty(submitCode) || !StringUtils.equals(verifyCode, submitCode.toLowerCase())){  
    90.             request.setAttribute("message_login""验证码不对");  
    91.             return resultPageURL;  
    92.         }  
    93.         UsernamePasswordToken token = new UsernamePasswordToken(username, password);  
    94.         token.setRememberMe(true);  
    95.         System.out.println("为了验证登录用户而封装的token为" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));  
    96.         //获取当前的Subject  
    97.         Subject currentUser = SecurityUtils.getSubject();  
    98.         try {  
    99.             //在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm运行必须的认证检查  
    100.             //每一个Realm都能在必要时对提交的AuthenticationTokens作出反应  
    101.             //所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,详细验证方式详见此方法  
    102.             System.out.println("对用户[" + username + "]进行登录验证..验证開始");  
    103.             currentUser.login(token);  
    104.             System.out.println("对用户[" + username + "]进行登录验证..验证通过");  
    105.             resultPageURL = "main";  
    106.         }catch(UnknownAccountException uae){  
    107.             System.out.println("对用户[" + username + "]进行登录验证..验证未通过,未知账户");  
    108.             request.setAttribute("message_login""未知账户");  
    109.         }catch(IncorrectCredentialsException ice){  
    110.             System.out.println("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");  
    111.             request.setAttribute("message_login""password不对");  
    112.         }catch(LockedAccountException lae){  
    113.             System.out.println("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");  
    114.             request.setAttribute("message_login""账户已锁定");  
    115.         }catch(ExcessiveAttemptsException eae){  
    116.             System.out.println("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");  
    117.             request.setAttribute("message_login""username或password错误次数过多");  
    118.         }catch(AuthenticationException ae){  
    119.             //通过处理Shiro的执行时AuthenticationException就能够控制用户登录失败或password错误时的情景  
    120.             System.out.println("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹例如以下");  
    121.             ae.printStackTrace();  
    122.             request.setAttribute("message_login""username或password不对");  
    123.         }  
    124.         //验证是否登录成功  
    125.         if(currentUser.isAuthenticated()){  
    126.             System.out.println("用户[" + username + "]登录认证通过(这里能够进行一些认证通过后的一些系统參数初始化操作)");  
    127.         }else{  
    128.             token.clear();  
    129.         }  
    130.         return resultPageURL;  
    131.     }  
    132.       
    133.       
    134.     /** 
    135.      * 用户登出 
    136.      */  
    137.     @RequestMapping("/logout")  
    138.     public String logout(HttpServletRequest request){  
    139.          SecurityUtils.getSubject().logout();  
    140.          return InternalResourceViewResolver.REDIRECT_URL_PREFIX + "/";  
    141.     }  
    142. }  
    以下是处理普通用户訪问的UserController.java

    [java] view plain copy
     print?

    在CODE上查看代码片派生到我的代码片

    1. package com.jadyer.controller;  
    2.   
    3. import javax.servlet.http.HttpServletRequest;  
    4.   
    5. import org.springframework.stereotype.Controller;  
    6. import org.springframework.web.bind.annotation.RequestMapping;  
    7.   
    8. @Controller  
    9. @RequestMapping("mydemo")  
    10. public class UserController {  
    11.     @RequestMapping(value="/getUserInfo")  
    12.     public String getUserInfo(HttpServletRequest request){  
    13.         String currentUser = (String)request.getSession().getAttribute("currentUser");  
    14.         System.out.println("当前登录的用户为[" + currentUser + "]");  
    15.         request.setAttribute("currUser", currentUser);  
    16.         return "/user/info";  
    17.     }  
    18. }  
    最后是用于生成登录验证码的VerifyCodeUtil.java

    [java] view plain copy
     print?

    在CODE上查看代码片派生到我的代码片

    1. package com.jadyer.util;  
    2.   
    3. import java.awt.Color;  
    4. import java.awt.Font;  
    5. import java.awt.Graphics;  
    6. import java.awt.image.BufferedImage;  
    7. import java.util.Random;  
    8.   
    9. /** 
    10.  * 验证码生成器 
    11.  * @see -------------------------------------------------------------------------------------------------------------- 
    12.  * @see 可生成数字、大写、小写字母及三者混合类型的验证码 
    13.  * @see 支持自己定义验证码字符数量,支持自己定义验证码图片的大小,支持自己定义需排除的特殊字符,支持自己定义干扰线的数量,支持自己定义验证码图文颜色 
    14.  * @see -------------------------------------------------------------------------------------------------------------- 
    15.  * @see 另外,给Shiro增加验证码有多种方式,也能够通过继承改动FormAuthenticationFilter类,通过Shiro去验证验证码 
    16.  * @see 而这里既然使用了SpringMVC,也为了简化操作,就使用此工具生成验证码,并在Controller中处理验证码的校验 
    17.  * @see -------------------------------------------------------------------------------------------------------------- 
    18.  * @create Sep 29, 2013 4:23:13 PM 
    19.  * @author 玄玉<http://blog.csdn.net/jadyer> 
    20.  */  
    21. public class VerifyCodeUtil {  
    22.     /** 
    23.      * 验证码类型为仅数字,即0~9 
    24.      */  
    25.     public static final int TYPE_NUM_ONLY = 0;  
    26.   
    27.     /** 
    28.      * 验证码类型为仅字母,即大写和小写字母混合 
    29.      */  
    30.     public static final int TYPE_LETTER_ONLY = 1;  
    31.   
    32.     /** 
    33.      * 验证码类型为数字和大写和小写字母混合 
    34.      */  
    35.     public static final int TYPE_ALL_MIXED = 2;  
    36.   
    37.     /** 
    38.      * 验证码类型为数字和大写字母混合 
    39.      */  
    40.     public static final int TYPE_NUM_UPPER = 3;  
    41.   
    42.     /** 
    43.      * 验证码类型为数字和小写字母混合 
    44.      */  
    45.     public static final int TYPE_NUM_LOWER = 4;  
    46.   
    47.     /** 
    48.      * 验证码类型为仅大写字母 
    49.      */  
    50.     public static final int TYPE_UPPER_ONLY = 5;  
    51.   
    52.     /** 
    53.      * 验证码类型为仅小写字母 
    54.      */  
    55.     public static final int TYPE_LOWER_ONLY = 6;  
    56.   
    57.     private VerifyCodeUtil(){}  
    58.       
    59.     /** 
    60.      * 生成随机颜色 
    61.      */  
    62.     private static Color generateRandomColor() {  
    63.         Random random = new Random();  
    64.         return new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255));  
    65.     }  
    66.       
    67.       
    68.     /** 
    69.      * 生成图片验证码 
    70.      * @param type           验证码类型,參见本类的静态属性 
    71.      * @param length         验证码字符长度,要求大于0的整数 
    72.      * @param excludeString  需排除的特殊字符 
    73.      * @param width          图片宽度(注意此宽度若过小,easy造成验证码文本显示不全,如4个字符的文本可使用85到90的宽度) 
    74.      * @param height         图片高度 
    75.      * @param interLine      图片中干扰线的条数 
    76.      * @param randomLocation 每一个字符的高低位置是否随机 
    77.      * @param backColor      图片颜色,若为null则表示採用随机颜色 
    78.      * @param foreColor      字体颜色,若为null则表示採用随机颜色 
    79.      * @param lineColor      干扰线颜色,若为null则表示採用随机颜色 
    80.      * @return 图片缓存对象 
    81.      */  
    82.     public static BufferedImage generateImageCode(int type, int length, String excludeString, int width, int height, int interLine, boolean randomLocation, Color backColor, Color foreColor, Color lineColor){  
    83.         String textCode = generateTextCode(type, length, excludeString);  
    84.         return generateImageCode(textCode, width, height, interLine, randomLocation, backColor, foreColor, lineColor);  
    85.     }  
    86.       
    87.   
    88.     /** 
    89.      * 生成验证码字符串 
    90.      * @param type          验证码类型,參见本类的静态属性 
    91.      * @param length        验证码长度,要求大于0的整数 
    92.      * @param excludeString 需排除的特殊字符(无需排除则为null) 
    93.      * @return 验证码字符串 
    94.      */  
    95.     public static String generateTextCode(int type, int length, String excludeString){  
    96.         if(length <= 0){  
    97.             return "";  
    98.         }  
    99.         StringBuffer verifyCode = new StringBuffer();  
    100.         int i = 0;  
    101.         Random random = new Random();  
    102.         switch(type){  
    103.             case TYPE_NUM_ONLY:  
    104.                 while(i < length){  
    105.                     int t = random.nextInt(10);  
    106.                     //排除特殊字符  
    107.                     if(null==excludeString || excludeString.indexOf(t+"")<0) {  
    108.                         verifyCode.append(t);  
    109.                         i++;  
    110.                     }  
    111.                 }  
    112.             break;  
    113.             case TYPE_LETTER_ONLY:  
    114.                 while(i < length){  
    115.                     int t = random.nextInt(123);  
    116.                     if((t>=97 || (t>=65&&t<=90)) && (null==excludeString||excludeString.indexOf((char)t)<0)){  
    117.                         verifyCode.append((char)t);  
    118.                         i++;  
    119.                     }  
    120.                 }  
    121.             break;  
    122.             case TYPE_ALL_MIXED:  
    123.                 while(i < length){  
    124.                     int t = random.nextInt(123);  
    125.                     if((t>=97 || (t>=65&&t<=90) || (t>=48&&t<=57)) && (null==excludeString||excludeString.indexOf((char)t)<0)){  
    126.                         verifyCode.append((char)t);  
    127.                         i++;  
    128.                     }  
    129.                 }  
    130.             break;  
    131.             case TYPE_NUM_UPPER:  
    132.                 while(i < length){  
    133.                     int t = random.nextInt(91);  
    134.                     if((t>=65 || (t>=48&&t<=57)) && (null==excludeString || excludeString.indexOf((char)t)<0)){  
    135.                         verifyCode.append((char)t);  
    136.                         i++;  
    137.                     }  
    138.                 }  
    139.             break;  
    140.             case TYPE_NUM_LOWER:  
    141.                 while(i < length){  
    142.                     int t = random.nextInt(123);  
    143.                     if((t>=97 || (t>=48&&t<=57)) && (null==excludeString || excludeString.indexOf((char)t)<0)){  
    144.                         verifyCode.append((char)t);  
    145.                         i++;  
    146.                     }  
    147.                 }  
    148.             break;  
    149.             case TYPE_UPPER_ONLY:  
    150.                 while(i < length){  
    151.                     int t = random.nextInt(91);  
    152.                     if((t >= 65) && (null==excludeString||excludeString.indexOf((char)t)<0)){  
    153.                         verifyCode.append((char)t);  
    154.                         i++;  
    155.                     }  
    156.                 }  
    157.             break;  
    158.             case TYPE_LOWER_ONLY:  
    159.                 while(i < length){  
    160.                     int t = random.nextInt(123);  
    161.                     if((t>=97) && (null==excludeString||excludeString.indexOf((char)t)<0)){  
    162.                         verifyCode.append((char)t);  
    163.                         i++;  
    164.                     }  
    165.                 }  
    166.             break;  
    167.         }  
    168.         return verifyCode.toString();  
    169.     }  
    170.   
    171.     /** 
    172.      * 已有验证码,生成验证码图片 
    173.      * @param textCode       文本验证码 
    174.      * @param width          图片宽度(注意此宽度若过小,easy造成验证码文本显示不全,如4个字符的文本可使用85到90的宽度) 
    175.      * @param height         图片高度 
    176.      * @param interLine      图片中干扰线的条数 
    177.      * @param randomLocation 每一个字符的高低位置是否随机 
    178.      * @param backColor      图片颜色,若为null则表示採用随机颜色 
    179.      * @param foreColor      字体颜色,若为null则表示採用随机颜色 
    180.      * @param lineColor      干扰线颜色,若为null则表示採用随机颜色 
    181.      * @return 图片缓存对象 
    182.      */  
    183.     public static BufferedImage generateImageCode(String textCode, int width, int height, int interLine, boolean randomLocation, Color backColor, Color foreColor, Color lineColor){  
    184.         //创建内存图像  
    185.         BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);  
    186.         //获取图形上下文  
    187.         Graphics graphics = bufferedImage.getGraphics();  
    188.         //画背景图  
    189.         graphics.setColor(null==backColor ? generateRandomColor() : backColor);  
    190.         graphics.fillRect(00, width, height);  
    191.         //画干扰线  
    192.         Random random = new Random();  
    193.         if(interLine > 0){  
    194.             int x = 0, y = 0, x1 = width, y1 = 0;  
    195.             for(int i=0; i<interLine; i++){  
    196.                 graphics.setColor(null==lineColor ? generateRandomColor() : lineColor);  
    197.                 y = random.nextInt(height);  
    198.                 y1 = random.nextInt(height);  
    199.                 graphics.drawLine(x, y, x1, y1);  
    200.             }  
    201.         }  
    202.         //字体大小为图片高度的80%  
    203.         int fsize = (int)(height * 0.8);  
    204.         int fx = height - fsize;  
    205.         int fy = fsize;  
    206.         //设定字体  
    207.         graphics.setFont(new Font("Default", Font.PLAIN, fsize));  
    208.         //写验证码字符  
    209.         for(int i=0; i<textCode.length(); i++){  
    210.             fy = randomLocation ? (int)((Math.random()*0.3+0.6)*height) : fy;  
    211.             graphics.setColor(null==foreColor ?

       generateRandomColor() : foreColor);  

    212.             //将验证码字符显示到图象中  
    213.             graphics.drawString(textCode.charAt(i)+"", fx, fy);  
    214.             fx += fsize * 0.9;  
    215.         }  
    216.         graphics.dispose();  
    217.         return bufferedImage;  
    218.     }  
    219. }  
  • 相关阅读:
    Python3之redis使用
    python3中urllib的基本使用
    最安全的api接口认证
    Python—I/O多路复用
    Python—RabbitMQ
    Python—sqlalchemy
    python操作MongoDB
    Python—进程、线程、协程
    推断client手机类型,并跳转到对应的app下载页面
    ExtJs--13-- Ext.apply(src,apply) 和 Ext.applyIf(src,apply) 两个方法的使用和差别比較
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/7327051.html
Copyright © 2011-2022 走看看