Spring3 Web Security機制

spring3.0.x中,加入了基於角色和url刪選的Web Security控制。


1. 首先在在web.xml中加入spring security filter:

<filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

<filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
 </filter-mapping>

實際上是用這個代理filter裏封裝了spring一系列security 相關的filter chain。依照處理順序分別爲:

1. org.springframework.security.web.context.SecurityContextPersistenceFilter 查找request的256字符的authority token,找到對應的用戶名。

2. org.springframework.security.web.authentication.logout.LogoutFilter 如果爲logout操作,清空授權token後往下filfter。

3. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter username和密碼方式驗證處理,會講前端form中傳來的username和password進行處理,重名成j_username, j_password後繼續往下filter.

4. org.springframework.security.openid.OpenIDAuthenticationFilter spring也支持openID方式的登錄,這裏會使用openID provider server進行驗證。

5. org.springframework.security.web.authentication.www.BasicAuthenticationFilter

6. org.springframework.security.web.savedrequest.RequestCacheAwareFilter

7. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter 

8. org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter 會記錄授權記過到cookie中,在客戶端cookie中登錄狀態緩存。

9. org.springframework.security.web.authentication.AnonymousAuthenticationFilter 檢查匿名用戶,即request中沒有j_userName的情況,會生成默認的匿名用戶名稱,只對permitAll的表達式通過。

10. org.springframework.security.web.session.SessionManagementFilter 在session中記錄或者清除授權結果。

11. org.springframework.security.web.access.ExceptionTranslationFilter 對filter12中授權失敗產生的異常進行處理。

12. org.springframework.security.web.access.intercept.FilterSecurityInterceptor 從下面的security url配置中找到access 表達式屬性,調用合適的expressionHandler驗證權限。


根據url的不同LogoutFilter, UsernamePassAuthenticationFilter, OpenIDAuthencationFilter會選擇跳過自己的filter, 調用filter chain往下執行。

直到SecurityContextHolderAwareRequestFilter中,它將原始的request wrap爲SecurityContextHolderAwareRequestWrapper類型。這個類型中關鍵方法有boolean isUserInRole(roleId),再次request wrapper中,spring會從Authentication auth = SecurityContextHolder.getContext().getAuthentication();獲取系統當前的已授權的信息。所以,我們通過SecurityContextHolder靜態方法,就可以查找到當前req線程權限授權(getContext()會返回req thead上下文的權限狀態)。


2. 然後在spring 配置文件中加入security的配置,例如:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
  -->

<!--
	This security file uses the default spring simple form login
-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <security:http auto-config="true" use-expressions="true" disable-url-rewriting="true">
        <security:intercept-url pattern="/newaccount.jsp*" access="permitAll"/>
        <security:intercept-url pattern="/app/newaccount*" access="permitAll"/>
        <security:intercept-url pattern="/login.jsp*" filters="none"/>
        <security:intercept-url pattern="/css/**" access="permitAll"/>
        <security:intercept-url pattern="/images/**" access="permitAll"/>
        <security:intercept-url pattern="/script/**" access="permitAll"/>
        <security:intercept-url pattern="/app/admin/**" access="hasRole('ROLE_ADMIN')"/>
        <!-- all urls must be authenticated -->
        <security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/>
        <security:openid-login user-service-ref="userService" authentication-failure-url="/login.jsp?authfail=openid">
            <security:attribute-exchange>
                <!-- Supported by MyOpenID.com -->
                <security:openid-attribute name="firstName" type="http://schema.openid.net/namePerson/first"/>
                <security:openid-attribute name="lastName" type="http://schema.openid.net/namePerson/last"/>
                <security:openid-attribute name="email" type="http://schema.openid.net/contact/email" required="true"/>
            </security:attribute-exchange>
        </security:openid-login>
        <security:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?authfail=form"/>
        <security:logout/>
        <security:remember-me/>
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider
                user-service-ref="userService">
            <security:password-encoder ref="passwordEncoder">
                <security:salt-source ref="saltSource"/>
            </security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>
    
    <!-- enable the spring security annotations -->
    <security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled">
        <security:expression-handler ref="expressionHandler"/>
    </security:global-method-security>
    
    <!-- override the default permissionEvaluator bean used by Spring Security with our custom RavePermissionEvaluator -->
    <bean id="expressionHandler" class="org.apache.rave.portal.security.impl.RaveMethodSecurityExpressionHandler">
        <property name="permissionEvaluator" ref="ravePermissionEvaluator" />            
    </bean>
</beans>

在<security:http> use-expressions屬性爲true時,aceess屬性判斷role,必須使用hasAnyRole或者hasRole的形式,或者permitAll,不用限制權限。

spring的授權框架會從你提供的userService bean中加載user,這個類必須實現org.springframework.security.core.userdetails.UserDetailsService類,在loadUserByUsername(name)方法中,從我們自己的user庫裏找到該名字的用戶,然後封裝成spring的UserDetails類型返回,如果沒有就拋UserNotFoundException。在UserDetails中,我們告訴這個找到的user密碼、具有的roles(封裝在spring GrantedAuthority類型中,其getAuthority()直接返回role id,就是security:http標籤中定義的role字符串,比如'ROLE_ADMIN')。


expressionHandlder bean中,spring提供了默認的表達式解析處理類org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler。這個類會對spring默認的權限判斷表達式,比如hasAnyRole('role1','role2')進行處理。如果我們要處理自己定製的表達式,集成個類後重寫即可。比如此處,我們用自己的permissionEvaluator,對spring傳給我們的不同目標資源進行分別處理。


3. 如上文所述,做自己的userService bean,繼承自org.springframework.security.core.userdetails.UserDetailsService



4. 若果要定製自己的登錄界面,<security:form-login>標籤中自己制定jsp頁面path。表單片段爲:

<form id="loginForm" name="loginForm" action="j_spring_security_check" method="post">
        <c:if test="${param['authfail'] eq 'form'}">
            <p class="error"><fmt:message key="page.login.usernamepassword.fail"/></p>
        </c:if>
        <fieldset>
            <p>
                <label for="usernameField"><fmt:message key="page.general.username"/></label>
                <input id="usernameField" type="text" name="j_username" autofocus="autofocus"/>
            </p>

            <p>
                <label for="passwordField"><fmt:message key="page.general.password"/></label>
                <input id="passwordField" type="password" name="j_password"/>
            </p>
            <p>
                <label for="remember_me" class="checkboxLabel">
                    <input type='checkbox' name='_spring_security_remember_me' id="remember_me"
                           value="true"/>
                    <fmt:message key="page.login.rememberme"/>
                </label>
            </p>
        </fieldset>
        <fieldset>
          <fmt:message key="page.login.usernamepassword.login" var="loginButtonText"/>
            <input type="submit" value="${loginButtonText}"/>
        </fieldset>
    </form>

page.login.usernamepassword.faild等爲登錄失敗後要顯示的Spring 屬性字符串。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章