S - Spring security初探

如今不少企業和開發團隊都使用了SSH2(Struts 2 +Spring 2.5 +Hibernate)框架來進行開發,  咱們或許已經習慣了強大的Spring Framework 全局配置管理,不能否認,Sping是一個很優秀的開源框架,可是因爲Spring3.0版本後強大的的註解式bean的誕生,Spring MVC框架這匹黑馬正悄然殺起,但今天Spring MVC不是主角,今天我和你們分享一個一樣隸屬於SpringSource 的安全框架——Spring Security, 下面的基於Spring MVC給你們分享一下Spring Security  的使用。雖然對它的接觸時間不長,參考了一些網上朋友的作法,但也按照個人理解把這個框架介紹介紹,不是很專業,還請你們不要介意 。html

    

     咱們知道,Web 應用的安全性包括用戶認證(Authentication)和用戶受權(Authorization)兩個部分。用戶認證指的是驗證某個用戶是否爲系統中的合法主體,也就是說用戶可否訪問該系統。用戶受權指的是驗證某個用戶是否有權限執行某個操做。在一個系統中,不一樣用戶所具備的權限是不一樣的。好比對一個資源來講,有的用戶只能進行讀取,而有的用戶能夠進行修改。通常來講,系統會爲不一樣的用戶分配不一樣的角色,而每一個角色則對應一系列的權限。java

 

     首先,咱們看web.xmlweb

 

Java代碼  收藏代碼算法

  1. <?xml version="1.0" encoding="UTF-8"?>  spring

  2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  數據庫

  3.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   express

  4.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  apache

  5.     <!-- 編碼統一最好放最上面,最早加載,防止亂碼-->  安全

  6.     <filter>  服務器

  7.         <filter-name>Set Character Encoding</filter-name>  

  8.         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  

  9.         <init-param>  

  10.             <param-name>encoding</param-name>  

  11.             <param-value>UTF-8</param-value>  

  12.         </init-param>  

  13.         <init-param>  

  14.             <param-name>forceEncoding</param-name>  

  15.             <param-value>true</param-value><!-- 強制進行轉碼 -->  

  16.         </init-param>  

  17.     </filter>  

  18.   

  19.     <filter-mapping>  

  20.         <filter-name>Set Character Encoding</filter-name>  

  21.         <url-pattern>/*</url-pattern>  

  22.     </filter-mapping>  

  23.     <!-- 而後接着是SpringSecurity必須的filter 優先配置,讓SpringSecurity先加載,防止SpringSecurity攔截失效-->  

  24.     <filter>  

  25.         <filter-name>springSecurityFilterChain</filter-name>  

  26.         <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  

  27.     </filter>  

  28.   

  29.     <filter-mapping>  

  30.         <filter-name>springSecurityFilterChain</filter-name>  

  31.         <url-pattern>/*</url-pattern>  

  32.     </filter-mapping>  

  33.   

  34.     <welcome-file-list>  

  35.         <welcome-file>index.jsp</welcome-file>  

  36.     </welcome-file-list>  

  37.   

  38.     <!--   

  39.     spring須要加載的配置文件  

  40. -->  

  41.     <context-param>  

  42.         <param-name>contextConfigLocation</param-name>  

  43.         <param-value>   

  44.     WEB-INF/classes/applicationContext.xml,  

  45.     WEB-INF/spring3-servlet.xml,  

  46.     WEB-INF/spring-security.xml   

  47.         </param-value>  

  48.     </context-param>  

  49.   

  50.     <listener>  

  51.         <listener-class>  

  52.             <!--     因此,要在web.xml下面配置好監聽,讓服務器啓動時就初始化改類,能夠獲得request   -->  

  53.             org.springframework.web.context.request.RequestContextListener  

  54.         </listener-class>  

  55.     </listener>  

  56.     <listener>  

  57.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  

  58.     </listener>  

  59.   

  60.   

  61.     <!--  

  62.         默認所對應的配置文件是WEB-INF下的{servlet-name}-servlet.xml,這裏即是:spring3-servlet.xml  

  63.     -->  

  64.     <servlet>  

  65.         <servlet-name>spring3</servlet-name>  

  66.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  

  67.         <load-on-startup>1</load-on-startup>  

  68.   

  69.     </servlet>  

  70.   

  71.     <servlet-mapping>  

  72.         <servlet-name>spring3</servlet-name>  

  73.         <!--  

  74.             這裏能夠用 / 但不能用 /*  

  75.             ,攔截了全部請求會致使靜態資源沒法訪問,因此要在spring3-servlet.xml中配置mvc:resources  

  76.         -->  

  77.         <url-pattern>/</url-pattern>  

  78.     </servlet-mapping>  

  79.   

  80.   

  81.   

  82. </web-app>  

 

     

 

    註釋已經寫了挺多,仍是稍微解釋一下要注意的地方,一個是UTF-8編碼轉換,這個最好加在最前面,讓它先生效,我在調試的時候就出過這種狀況,web.xml裏的其餘配置都正常生效了,可是編碼死活不行,一中文就亂碼,鬱悶了老半天,而後突發奇想,是否是web.xml裏先聲明的配置先生效,後聲明的後生效?接着實踐,果真不出我所料,把編碼轉換加在前面,一切正常。。。。我那個暈。。。

 

 

 

關於Spirng MVC的就不說了,那些數據訪問、業務和控制層那些的東東就自個研究去了吧。。這不是今天的重點。

 

 接着是spring-security.xml

Java代碼  收藏代碼

  1. <?xml version="1.0" encoding="UTF-8"?>  

  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  

  3.     xmlns:security="http://www.springframework.org/schema/security"  

  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans   

  5.             http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  

  6.             http://www.springframework.org/schema/security   

  7.             http://www.springframework.org/schema/security/spring-security-3.0.xsd">  

  8.   

  9.     <!--  Spring-Security 的配置 -->  

  10.     <!--  

  11.         注意use-expressions=true.表示開啓表達式,不然表達式將不可用. see:http://www.family168.com/tutorial/springsecurity3/html/el-access.html  

  12.     -->  

  13.     <security:http auto-config="true" use-expressions="false" access-denied-page="/user/login_failure.html">  

  14.         <!--容許全部人訪問-->  

  15.         <!--     <security:intercept-url pattern="/**" access="permitAll" />-->  

  16.         <!--容許ROLE_ADMIN權限訪問-->  

  17.         <security:intercept-url pattern="/user/findAll.html" access="ROLE_ADMIN" />  

  18.         <!--容許ROLE_ADMIN權限訪問-->  

  19.         <security:intercept-url pattern="/user/**" access="ROLE_ADMIN" />  

  20.         <!--容許ROLE_USER權限訪問-->  

  21.         <security:intercept-url pattern="/success.jsp" access="ROLE_USER,ROLE_ADMIN" />  

  22.         <!--容許IS_AUTHENTICATED_ANONYMOUSLY匿名訪問-->  

  23.         <security:intercept-url pattern="/anonymously.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />  

  24.           

  25.         <!-- filters="none"  不過濾這些資源-->  

  26.         <security:intercept-url pattern="/js/**" filters="none" />  

  27.         <security:intercept-url pattern="/index.jsp" filters="none" />  

  28.   

  29.         <!-- login-page:默認指定的登陸頁面. authentication-failure-url:出錯後跳轉頁面. default-target-url:成功登錄後跳轉頁面 -->  

  30.         <security:form-login login-page="/index.jsp" authentication-failure-url="/user/login_failure.html"  

  31.             default-target-url="/success.jsp" />  

  32.         <!--  

  33.             invalidate-session:指定在退出系統時是否要銷燬Session。logout-success-url:退出系統後轉向的URL。logout-url:指定了用於響應退出系統請求的URL。其默認值爲:/j_spring_security_logout。  

  34.         -->  

  35.         <security:logout invalidate-session="true" logout-success-url="/index.jsp" logout-url="/j_spring_security_logout" />  

  36.         <!--  

  37.             max-sessions:容許用戶賬號登陸的次數。範例限制用戶只能登陸一次。exception-if-maximum-exceeded:  

  38.             默認爲false,此值表示:用戶第二次登陸時,前一次的登陸信息都被清空。當exception-if-maximum-exceeded="true"時系統會拒絕第二次登陸。  

  39.         -->  

  40.   

  41.         <security:session-management>  

  42.             <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1" />  

  43.         </security:session-management>  

  44.   

  45.     </security:http>  

  46.     <!-- 指定一個自定義的authentication-manager :customUserDetailsService -->  

  47.     <security:authentication-manager>  

  48.         <security:authentication-provider user-service-ref="customUserDetailsService">  

  49.             <security:password-encoder ref="passwordEncoder" />  

  50.         </security:authentication-provider>  

  51.     </security:authentication-manager>  

  52.   

  53.     <!-- 對密碼進行MD5編碼 -->  

  54.     <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />  

  55.   

  56.     <!--   

  57.         經過 customUserDetailsService,Spring會控制用戶的訪問級別.  

  58.         也能夠理解成:之後咱們和數據庫操做就是經過customUserDetailsService來進行關聯.  

  59.      -->  

  60.     <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" />  

  61.     <!-- 自定義登錄錯誤提示,能夠取出mymessages.properties的國際化消息-->  

  62.     <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  

  63.         <property name="basename" value="classpath:org/yzsoft/springmvcdemo/mymessages" />  

  64.     </bean>  

  65.     <bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" />  

  66. </beans>  

  

 

 這個多解釋一下,首先

 

Java代碼  收藏代碼

  1. <security:intercept-url pattern="/findAll.html" access="hasRole('ROLE_ADMIN')" />  

  2.        <security:intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" />  

  3.        <security:intercept-url pattern="/anonymously.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />  

 這個是權限控制,聲明瞭擁有什麼權限能夠訪問哪些資源,這個配置的是有ROLE_ADMIN權限的才能夠訪問/findAll.html,至於這個ROLE_ADMIN從哪來,呆會再解釋。或者像第二句同樣配置也能夠:擁有ROLE_ADMIN權限的才能夠訪問/user/下的全部資源,不然都會拋出AccessDeniedException 。

IS_AUTHENTICATED_ANONYMOUSLY就是匿名訪問的意思,這個相信都懂的。。。

 

而後是登錄和安全退出

Java代碼  收藏代碼

  1. <security:form-login login-page="/index.jsp" authentication-failure-url="/user/login_failure.html"default-target-url="/user/findAll.html" />  

  2. <security:logout invalidate-session="true" logout-success-url="/index.jsp" logout-url="/user/login_failure.html" />  

 解釋下上面一句,相信看也能看出來了的,login-page:默認指定的登陸頁面. authentication-failure-url:出錯後跳轉頁面(包括那些個啥用戶名密碼錯誤吖。。啥啥啥的。). default-target-url:成功登錄後跳轉頁面 (這裏我直接跳到的控制器去查數據列表)。   

 

 接着:invalidate-session:指定在退出系統時是否要銷燬Session。logout-success-url:退出系統後轉向的URL。logout-url:指定了用於響應退出系統請求的URL。其默認值爲:/j_spring_security_logout。

 

接下來是一個比較不錯的功能:是否容許同一用戶多處登錄

Java代碼  收藏代碼

  1. <security:session-management>  

  2.      <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1" />  

  3. </security:session-management>  

exception-if-maximum-exceeded:
   默認爲false,此值表示:用戶第二次登陸時,前一次的登陸信息都被清空。當error-if-maximum-exceeded="true"時系統會拒絕第二次登陸。 

max-sessions:容許用戶賬號登陸的次數,這裏咱們容許一次登錄。這裏咱們作個實驗吧,看看是否是真的生效了,請看圖


 

 

這裏咱們看到,當同一個帳號多處登錄時,就會報出Maximum sessions of 1 for this principal exceeded 的錯誤,固然,正式使用咱們換成mymessages.properties裏的咱們自定義的國際化消息,這樣人性化一點。
 

 好,咱們往下看,接着就是應用咱們實際項目裏的自定義用戶權限了

Java代碼  收藏代碼

  1. <security:authentication-manager>  

  2.      <security:authentication-provider user-service-ref="customUserDetailsService">  

  3.          <security:password-encoder ref="passwordEncoder" />  

  4.      </security:authentication-provider>  

  5. </security:authentication-manager>  

  6.   

  7.     <!-- 對密碼進行MD5編碼 -->  

  8. <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />  

  9. <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" />  

 首先是<security:authentication-manager>是指定咱們自定義的身份驗證策略,這裏咱們用customUserDetailsService這個bean,就是指向咱們CustomUserDetailsService.java這個類。而後<security:password-encoder>指定咱們密碼使用MD5進行編碼,調用Spring Security自帶的MD5加密類。固然,還有加鹽MD5或咱們本身寫的加密算法等安全性更加高的密碼策略。這個按項目實際使用配置吧。

 

而後看到咱們的CustomUserDetailsService.java

Java代碼  收藏代碼

  1. package org.yzsoft.springmvcdemo.util;  

  2.   

  3. import java.util.ArrayList;  

  4. import java.util.Collection;  

  5. import java.util.List;  

  6.   

  7. import org.apache.log4j.Logger;  

  8. import org.springframework.beans.factory.annotation.Autowired;  

  9. import org.springframework.dao.DataAccessException;  

  10. import org.springframework.security.core.GrantedAuthority;  

  11. import org.springframework.security.core.authority.GrantedAuthorityImpl;  

  12. import org.springframework.security.core.userdetails.User;  

  13. import org.springframework.security.core.userdetails.UserDetails;  

  14. import org.springframework.security.core.userdetails.UserDetailsService;  

  15. import org.springframework.security.core.userdetails.UsernameNotFoundException;  

  16. import org.yzsoft.springmvcdemo.serviceimpl.UsersServiceImpl;  

  17. import org.yzsoft.springmvcdemo.vo.TUsers;  

  18.   

  19. /** 

  20.  * 一個自定義的類用來和數據庫進行操做. 即之後咱們要經過數據庫保存權限.則須要咱們繼承UserDetailsService 

  21.  *  

  22.  * @author  

  23.  *  

  24.  */  

  25. public class CustomUserDetailsService implements UserDetailsService {  

  26.   

  27.     protected static Logger logger = Logger.getLogger("service");//log4j,不用解釋了吧。。  

  28.     @Autowired  

  29.     private UsersServiceImpl usersService;  

  30.   

  31.     public UsersServiceImpl getUsersService() {  

  32.         return usersService;  

  33.     }  

  34.   

  35.     public void setUsersService(UsersServiceImpl usersService) {  

  36.         this.usersService = usersService;  

  37.     }  

  38.   

  39.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {  

  40.   

  41.         UserDetails user = null;  

  42.   

  43.         try {  

  44.   

  45.             // 搜索數據庫以匹配用戶登陸名.  

  46.             // 咱們能夠經過dao使用Hibernate來訪問數據庫  

  47.             System.out.println(username + "   用戶頁面輸入的用戶名");  

  48.             TUsers tusers = this.usersService.findByUsername(username);  

  49.             System.out.println(tusers.getUsername() + "   數據庫取出的用戶名");  

  50.             // Populate the Spring User object with details from the dbUser  

  51.             // Here we just pass the username, password, and access level  

  52.             // getAuthorities() will translate the access level to the correct  

  53.             // role type  

  54.             // 用戶名、密碼、是否啓用、是否被鎖定、是否過時、權限  

  55.             user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), truetruetruetrue, getAuthorities(Integer.parseInt(tusers.getRole())));  

  56.                

  57.                

  58.         } catch (Exception e) {  

  59.             logger.error("用戶信息錯誤!");  

  60.             throw new UsernameNotFoundException("異常處理:檢索用戶信息未經過!");  

  61.         }  

  62.            

  63.         return user;  

  64.     }  

  65.   

  66.     /** 

  67.      * 得到訪問角色權限列表 

  68.      *  

  69.      * @param access 

  70.      * @return  

  71.      */  

  72.     public Collection<GrantedAuthority> getAuthorities(Integer role) {  

  73.         System.out.println("取得的權限是  :" + role);  

  74.         List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();  

  75.   

  76.         // 全部的用戶默認擁有ROLE_USER權限  

  77.         if (role == 0) {  

  78.             System.out.println("普通用戶");  

  79.             logger.debug("取得普通用戶權限-->");  

  80.             authList.add(new GrantedAuthorityImpl("ROLE_USERS"));  

  81.         }  

  82.         // 若是參數role爲1.則擁有ROLE_ADMIN權限  

  83.         if (role == 1) {  

  84.             logger.debug("取得ADMIN用戶權限-->");  

  85.             authList.add(new GrantedAuthorityImpl("ROLE_ADMIN"));  

  86.         }  

  87.         System.out.println(authList.size()+"  權限列表長度");  

  88.         return authList;  

  89.     }  

  90. }  

 這裏就是把咱們從數據庫裏面取得的用戶權限和Spring Security的配置進行橋接,還記得上面配置文件裏的ROLE_ADMIN吧,就是從這裏來的,很奇怪的是,這個必須設置成ROLE_ 開頭纔有效。。鬱悶。。。

這裏我剛開始有一個疑惑,咱們看這2句

Java代碼  收藏代碼

  1. TUsers tusers = this.usersService.findByUsername(username);  

  2. user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), truetruetruetrue, getAuthorities(Integer.parseInt(tusers.getRole())));  

 這裏根本不須要用戶輸入的密碼,只要了用戶名,而後直接根據用戶名去取權限,就直接設置進Spring Security的User對象裏面去,我不由一身冷汗,這不至關於說有了用戶名就直接去查數據庫麼,並且是不用密碼的。。。。

但通過查看官方文檔和網上的解釋,這才放心,原來是這樣的,Spring Security的確是直接根據用戶名去查,可是查得出來的Spring Security  User對象以後,它會根據這個對象的屬性值去數據庫查詢與這個對象匹配的數據,咱們這裏設置的是(用戶名,密碼,是否啓用、是否被鎖定、是否過時、權限。。。),那麼若是數據庫存在這個對象,就返回真,不然返回假,這樣也就不用擔憂了,徹底可靠。就是咱們在前臺要作好限制,不能給用戶不輸密碼就訪問, 否則擠爆你數據庫鏈接。。。。。

 

 

 最後登錄頁面index.jsp

Java代碼  收藏代碼

  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>  

  2. <%  

  3. String path = request.getContextPath();  

  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";  

  5. %>  

  6.   

  7. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  

  8. <html>  

  9.   <head>  

  10.     <base href="<%=basePath%>">  

  11.       

  12.     <title>My JSP 'index.jsp' starting page</title>  

  13.     <meta http-equiv="pragma" content="no-cache">  

  14.     <meta http-equiv="cache-control" content="no-cache">  

  15.     <meta http-equiv="expires" content="0">      

  16.     <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  

  17.     <meta http-equiv="description" content="This is my page">   

  18.   </head>  

  19.     

  20.   <body>  

  21.     用戶登錄 <br>  

  22.      ${SPRING_SECURITY_LAST_EXCEPTION.message}  

  23.       <form action="j_spring_security_check" method="post">  

  24.         USERNAME:<input type="text" name="j_username" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" /><br/>  

  25.         PASSWORD:<input type="password" name="j_password" value="" /><br/>  

  26.         <input type="checkbox" name="_spring_security_remember_me" />兩週以內沒必要登錄(這個功能沒有作的)<br/>  

  27.         <input type="submit">       

  28.     </form>  

  29.   </body>  

  30. </html>  

 

這裏我仍是使用Spring Security默認的j_username和j_password,表單目標也用默認的j_spring_security_check,會默認跳到Spring Security進行攔截。其餘的應該不用解釋了吧。。。。

 

最後 ,咱們整理一下Spring Security的整個控制過程:

 

——>用戶登錄

——> <security:authentication-manager> 攔截

——>交給customUserDetailsService處理,而且聲明密碼採用MD5策略

——>根據輸入的用戶名去數據庫查這條記錄,驗證身份

——>取出該條記錄的用戶權限(鎖定、禁用、過時和實際權限等)

——>根據取得的權限列表去security:intercept-url匹配、受權,而後判斷是否放行。

這就完成了一整個的權限控制流程。

 

接下來咱們來測試一下看是否真的生效了:

一、測試匿名訪問頁面,直接地址欄訪問:

 

 

 

 

 

二、普通用戶登錄



  

 

 

 

而後訪問後臺管理頁面,跳回了登錄頁



 

 

 

 

 

三、管理員用戶登錄

 

 

 

 

退出後成功跳轉回登錄頁,點擊後退再執行其餘操做,這時候session已經註銷了的,不能執行,又跳回了登錄頁。是咱們想要的效果,OK,成功了。

 

 

 好了,Spring Security的簡單使用就講到這裏,其實這只是Spring Security的一小部分,並且這裏我尚未用權限表對用戶權限進行專門的管理,不少東西仍是用Spring Security 默認的,還有Spring Security CAS (單點登錄)以及更加高級的權限控制和更完善的Spring Security 配置,之後咱們再慢慢去研究吧。發現Spring Security 這個技術不只簡化了咱們的用戶權限管理,要知道咱們作管理系統的時候這是個大問題,也差很少顛覆了我一向以來用戶權限管理的觀念,可是掌握了這種思惟以後,又發現,其實,程序並非只有一種實現方式,它激發了我寫程序時要去尋找多種解決方案的想法。學習的路上,就是要不斷推翻本身固有的思惟,去見識多種新事物,纔能有進步。

相關文章
相關標籤/搜索