1. 程式人生 > >Spring-security框架

Spring-security框架

spring-security框架

1. 概述
    spring-security一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方式的安全框架
    簡單的說就是一個常用於後臺控制權限的框架
    主要分為:使用者認證,使用者授權兩部分

2. 快速入門
    1. 入門準備
        1.1 匯入依賴jar包
            spring-security-web
            spring-security-config
            spring-security-taglibs(在jsp中引入可以在jsp頁面進行許可權認證並對頁面進行限制)
            spring-security-core

        1.2 在web.xml中建立filter以及listener

            * listener與context-param是為了在web專案執行的時候載入spring-security的配置檔案,交給spring管理物件

            <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-security.xml</param-value>
            </context-param>

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

           * filter是為了攔截url,可以進行後續的使用者認證以及使用者許可權管理 
           * 特別注意: springSecurityFilterChain這個名字不能更改. 因為原始碼中在載入時名字寫死了是這個

            <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>

        1.3 在spring-security.xml中配置核心檔案

        <?xml version="1.0" encoding="UTF-8"?>
        <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.xsd          
            http://www.springframework.org/schema/security          
            http://www.springframework.org/schema/security/spring-security.xsd">

            <!--下面三個註解是配置後可以在controller上加來控制不同角色的訪問許可權-->
            <!--在spring-security.xml中開啟jsr250的註解支援,使用這個註解需要匯入依賴-->
            <!--<security:global-method-security jsr250-annotations="enabled"/>-->

            <!--在spring-security.xml中開啟自帶表示式的註解-->
            <!--<security:global-method-security pre-post-annotations="enabled"/>-->

            <!--開啟@Secured註解-->
            <security:global-method-security secured-annotations="enabled"/>



            <!-- 配置不攔截的資源 -->
            <security:http pattern="/login.jsp" security="none"/>
            <security:http pattern="/failer.jsp" security="none"/>
            <security:http pattern="/css/**" security="none"/>
            <security:http pattern="/img/**" security="none"/>
            <security:http pattern="/plugins/**" security="none"/>

            <!-- 
            配置具體的規則 
            auto-config="true"    不用自己編寫登入的頁面,框架提供預設登入頁面
            use-expressions="false"    是否使用SPEL表示式,當使用自帶表示式時候,可以設定為true,access寫為hasRole('ROLE_USER')
            可以使用||或者&&
            -->
            <security:http auto-config="true" use-expressions="false">
            <!-- 配置具體的攔截的規則 pattern="請求路徑的規則" access="訪問系統的人,必須有ROLE_USER的角色" -->
            <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN"/>

            <!-- 定義跳轉的具體的頁面
            login-processing-url為登入時候攔截的路徑,要與form表單中的action相同
            default-target-url 預設許可權認證通過時候的跳轉頁面
            -->
            <security:form-login  
                login-page="/login.jsp"
                login-processing-url="/login.do"
                default-target-url="/index.jsp"
                authentication-failure-url="/failer.jsp"
            />

            <!-- 關閉跨域請求 -->
            <security:csrf disabled="true"/>

            <!-- 退出的相關的方法,以及退出到的頁面 -->
            <security:logout invalidate-session="true" logout-url="/logout.do" logout-success-url="/login.jsp" />

            </security:http>

            <!-- 切換成資料庫中的使用者名稱和密碼 -->
            <security:authentication-manager>
            <security:authentication-provider user-service-ref="userService">
                <!-- 配置加密的方式,當資料庫中存放的是明文的時候,不要加,並且驗證時候在密碼前加 {noop} -->
                <security:password-encoder ref="passwordEncoder"/>
            </security:authentication-provider>
            </security:authentication-manager>

            <!-- 配置加密類 -->
            <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

            <!-- 提供了入門的方式,在記憶體中存入使用者名稱和密碼 ,而通常來說,這些應該放置在資料庫中
            <security:authentication-manager>
            <security:authentication-provider>
                <security:user-service>
                    <security:user name="admin" password="{noop}admin" authorities="ROLE_USER"/>
                </security:user-service>
            </security:authentication-provider>
            </security:authentication-manager>
            -->
            <!--當使用自帶表示式的註解的時候,開啟對錶達式的支援,或者配置一個下面的bean-->
            <bean id="webexpressionHandler"
              class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>


        </beans>
    2. 配置完畢後,執行專案.
    spring-security會攔截病跳轉到一個驗證的頁面,當配置了登陸介面時,會跳轉到自定義的頁面.輸入正確的username與password時即可

3. 部分詳解
    1. 自定義service實現驗證步驟
        spring-security定義了一系列的介面,並且代替了mvc中的controller,我們只需實現service層以及dao層的程式碼即可
        1.1 service層的實現
            * 定義一個介面 extends UserDetailsService
              ps:為了擴充套件性,所以先使用介面整合UserDetailsService
            * 實現自定義的介面並重寫loadUserByUsername(String username)方法
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                //查詢出一個userInfo
                UserInfo userInfo = null;
                try {
                     userInfo = userDao.findByUsername(username);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //將自己的物件封裝成為UserDetails
                //User是UserDetails的一個實現類
                //不對userinfo中的status進行驗證
                //User user = new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),getAuthorities(userInfo.getRoles()));
                //對userinfo中的status進行驗證未加密
                //User user = new User(userInfo.getUsername(),"{noop}"+userInfo.getPassword(),userInfo.getStatus()==1?true:false,
                //加密後的不用加noop
                User user = new User(userInfo.getUsername(),userInfo.getPassword(),userInfo.getStatus()==1?true:false,
                    true,true,true,getAuthorities(userInfo.getRoles()));
                return user;
                }

                public List<SimpleGrantedAuthority> getAuthorities(List<Role> roles){
                List<SimpleGrantedAuthority> list = new ArrayList<SimpleGrantedAuthority>();
                for (Role role : roles) {
                    list.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
                }
                return list;
                }

    2.伺服器端方法級許可權控制
        * 開啟註解
            配置檔案
            <security:global-method-security jsr250-annotations="enabled"/>
            <security:global-method-security secured-annotations="enabled"/>
            <security:global-method-security pre-post-annotations="disabled"/>
            註解開啟
            @EnableGlobalMethodSecurity:Spring Security預設是禁用註解的,要想開啟註解,需要在繼承
            WebSecurityConfigurerAdapter的類上加 @EnableGlobalMethodSecurity註解並將AuthenticationManager定義為Bean。

        * jsr250註解

            @RolesAllowed表示訪問對應方法時所應該具有的角色
                示例: @RolesAllowed({"USER", "ADMIN"}) 該方法只要具有"USER", "ADMIN"任意一種許可權就可以訪問。
                這裡可以省略字首ROLE_,實際的許可權可能是ROLE_ADMIN
            @PermitAll表示允許所有的角色進行訪問,也就是說不進行許可權控制
            @DenyAll是和PermitAll相反的,表示無論什麼角色都不能訪問

            常用寫法:
                @RolesAllowed("ADMIN")

        * 支援表示式的註解
            * 表示式註解需要開啟表示式的支援
            <!--當使用自帶表示式的註解的時候,開啟對錶達式的支援,或者配置一個下面的bean-->
                <bean id="webexpressionHandler"
              class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

            @PreAuthorize 在方法呼叫之前,基於表示式的計算結果來限制對方法的訪問
                示例:
                @PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
                void changePassword(@P("userId") long userId ){... }
                這裡表示在changePassword方法執行之前,判斷方法引數userId的值是否等於principal中儲存的當前使用者的userId,
                或者當前使用者是否具有ROLE_ADMIN許可權,兩種符合其一,就可以訪問該方法。
            @PostAuthorize 允許方法呼叫,但是如果表示式計算結果為false,將丟擲一個安全性異常
                示例:
                    @PostAuthorize
                User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");
            @PostFilter 允許方法呼叫,但必須按照表達式來過濾方法的結果
            @PreFilter 允許方法呼叫,但必須在進入方法之前過濾輸入值

            Authentication可以簡單理解為: 儲存的認證物件
            Principle 可以簡單理解為: 在認證物件中儲存的使用者物件.


            常用寫法:
                @PreAuthorize("authentication.principal.username == 'tom'")    //使用者名稱是否為tom
                @PreAuthorize(hasRole('ROLE_ADMIN')")//使用者是否具有ROLE_ADMIN角色,這裡的ROLE_不能省略
        * @Secured註解
            @Secured註解標註的方法進行許可權控制的支援,其值預設為disabled
                常用寫法:
                    @Secured("ROLE_USER")
                    public Account readAccount(Long id);

    3.頁面許可權標籤
        * 先引入taglib
            <%@taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

        * 在jsp中我們可以使用以下三種標籤來控制權限
            authentication
                <security:authentication property="" htmlEscape="" scope="" var=""/> 
                注意property=""中的值,多寫為 principal.username

                property: 只允許指定Authentication所擁有的屬性,可以進行屬性的級聯獲取,如principal.username
                不允許直接通過方法進行呼叫
                htmlEscape:表示是否需要將html進行轉義。預設為true。
                scope:與var屬性一起使用,用於指定存放獲取的結果的屬性名的作用範圍,預設我pageContext。Jsp中擁
                有的作用範圍都進行進行指定
                var: 用於指定一個屬性名,這樣當獲取到了authentication的相關資訊後會將其以var指定的屬性名進行存
                放,預設是存放在pageConext中

                常用寫法:
                <security:authentication property="principal.username"/>

            authorize
                <security:authorize access="" method="" url="" var=""></security:authorize>

                access: 需要使用表示式來判斷許可權,當表示式的返回結果為true時表示擁有對應的許可權
                method:method屬性是配合url屬性一起使用的,表示使用者應當具有指定url指定method訪問的許可權,
                method的預設值為GET,可選值為http請求的7種方法
                url:url表示如果使用者擁有訪問指定url的許可權即表示可以顯示authorize標籤包含的內容
                var:用於指定將許可權鑑定的結果存放在pageContext的哪個屬性中


                常用寫法:
                <security:authorize access="hasRole('ADMIN')"></security:authorize>

            accesscontrollist
                accesscontrollist標籤是用於鑑定ACL許可權的。其一共定義了三個屬性:hasPermission、domainObject和var,
                其中前兩個是必須指定的
                <security:accesscontrollist hasPermission="" domainObject="" var=""></security:accesscontrollist>

                hasPermission:hasPermission屬性用於指定以逗號分隔的許可權列表
                domainObject:domainObject用於指定對應的域物件
                var:var則是用以將鑑定的結果以指定的屬性名存入pageContext中,以供同一頁面的其它地方使用