權限控制(Spring Security框架)入門詳解+實際應用

一、權限控制相關概念

1.1、認證和授權的概念

我們知道一個系統的不同用戶所擁有的系統權限是不同的,並且每個用戶想要登錄系統都需要進行用戶名和密碼的校驗,驗證成功後才能進入系統。

所以總結出認證和授權的概念如下:

  • 認證:系統提供的用於識別用戶身份的功能,通常提供用戶名和密碼進行登錄其實就是在進行認證,認證的目的是讓系統知道你是誰。
  • 授權:用戶認證成功後,需要爲用戶授權,其實就是指定當前用戶可以操作哪些功能。

總結:對一個應用的後臺系統進行權限控制,其本質就是對用戶進行認證和授權的行爲。

1.2、權限模塊數據模型

既然已經知道了認證和授權的概念,那麼該如何從技術角度去實現呢?
這就需要有一套完善的權限模塊數據模型。 對應到實際的操作中就是需要有一套數據表結構來支撐認證和授權的實現。

最常用的權限模塊數據模型就是
四個實體表: 用戶表+角色表+權限表+菜單表
三個關係表:用戶角色關係表+角色權限關係表+角色菜單關係表

爲什麼用引入角色表呢?
試想如果直接讓用戶與權限表關聯 那麼權限有很多種 用戶數量則更多那麼用戶表中的每個用戶都要關聯不同的權限 這樣會造成用戶表的數據大量冗餘 也不利於數據的管理 所以引入角色表 讓不同的角色關聯不同的權限 用戶只需要與相應的角色相關聯即可

在這裏插入圖片描述
從上圖可以看出 角色表非常關鍵 其餘6個表都直接或間接與角色表關聯

後臺的授權過程:
首先用戶必須完成認證之後纔可以進行授權,首先根據用戶信息查詢其角色信息,再根據角色信息查詢對應的菜單,這樣就確定了用戶能夠看到哪些菜單。然後再根據用戶的角色查詢對應的權限,這樣就確定了用戶擁有哪些權限。所以授權的過程會用到上面的全部7張表。
而認證過程只需要用到用戶表即可。

二、Spring Security的介紹和使用

2.1、Spring Security簡介

Spring Security是 Spring提供的安全認證服務的框架。 使用Spring Security可以幫助我們來簡化認證
和授權的過程。 另外還有一個常用的權限框架:Apache的shiro框架。
這篇文章只介紹Spring Security。

Spring Security官網

要想使用Spring Security首先要在項目中引入對應jar包

下面以Maven工程爲例講解Spring Security的基本使用:
Spring Security對應的Maven座標(使用的是5.0.5版本)

<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-web</artifactId>
 <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-config</artifactId>
 <version>5.0.5.RELEASE</version>
</dependency>

2.2、Spring Security快速入門

2.2.1、搭建maven工程 (配置文件能讀懂即可)

引入相關座標
pom.xml

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

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.kinggm520</groupId>
    <artifactId>SpringSecurity</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>SpringSecurity Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.0.5.RELEASE</spring.version>
        <spring.security.version>5.0.5.RELEASE</spring.security.version>
        <servlet-api.version>2.5</servlet-api.version>

    </properties>


    <dependencies>

        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- 安全框架 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>${servlet-api.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <port>85</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.2.2、配置web.xml

在web.xml中主要配置SpringMVC的DispatcherServlet和用於整合第三方框架的DelegatingFilterProxy,用於整合Spring Security。
web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <filter>
    <!--
      DelegatingFilterProxy用於整合第三方框架
      整合Spring Security時過濾器的名稱必須爲springSecurityFilterChain,
   否則會拋出NoSuchBeanDefinitionException異常
    -->
    <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>

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 指定加載的配置文件 ,通過參數contextConfigLocation加載 -->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-security.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

</web-app>

2.2.3、配置spring-security.xml

spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       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">


    <!-- auto-config:自動配置,如果設置爲true,表示自動應用一些默認配置,比如框架會提供一個默認的登錄頁面  use-expressions:是否使用spring security提供的表達式來描述權限 -->
    <security:http auto-config="true" use-expressions="true">
        <!--配置攔截規則,/** 表示攔截所有請求  注意/*只能攔截一層-->
        <!-- pattern:描述攔截規則 asscess:指定所需的訪問角色或者訪問權限-->
        <security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
    </security:http>
    <!--配置認證管理器-->
    <security:authentication-manager>
        <!--配置認證提供者-->
        <security:authentication-provider>
            <!--配置一個具體的用戶,後期需要從數據庫查詢用戶   {noop}1234固定寫法 表示明文密碼1234 -->
            <security:user-service>
                <security:user name="admin" password="{noop}1234"
                               authorities="ROLE_ADMIN"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

入門案例整體結構:
在這裏插入圖片描述

2.2.4、入門案例測試

在這裏插入圖片描述
從上圖可以看出 當我們訪問http://localhost:85/SpringSecurity/index.html時
並沒有進入index.html頁面 而是直接進入了一個SpringSecurity框架爲我們準備好的一個登陸頁面
當輸入 一開始配置好的用戶名 admin 密碼1234 點擊登錄後 才進入到index.html頁面

注意:

分析案例的執行過程,當訪問http://localhost:85/SpringSecurity/index.html時 首先會進入SpringSecurity框架,由於SpringSecurity框架配置的是攔截所有請求,且要求當前用戶必須具有ROLE_ADMIN這個角色,我們一開始訪問index.html時並沒有進行認證 狀態是匿名用戶,此時框架就會自動把頁面跳轉到認證(默認的登錄頁面)頁面。我們在配置文件中已經配置了一個用戶。

<!--配置一個具體的用戶,後期需要從數據庫查詢用戶   {noop}1234固定寫法 表示明文密碼1234 -->
<security:user-service>
<security:user name="admin" password="{noop}1234" authorities="ROLE_ADMIN"/>
</security:user-service>

如果輸入錯誤的密碼會提示登錄失敗
在這裏插入圖片描述
只有輸入了正確的密碼,並且當前認證通過的用戶具有ROLE_ADMIN權限時 纔會跳轉到index.html頁面

上面就是SpringSecurity的基本入門案例
下面來講解SpringSecurity在實際項目中的應用

三、SpringSecurity在實際項目中應用

在上面的入門案例中Spring Security將我們
項目中的所有資源都保護了(我們訪問所有資源都需要經過Spring Security的過濾器)起來,要訪問這些資源必須要完成認證而且需要具有ROLE_ADMIN角色。

而我們在實際項目中肯定不會過濾所有的請求 也不會使用框架提供的默認登錄界面(太醜了)
更重要的是用戶密碼一定會進行加密處理。

下面將入門案例改進 以達到實際項目的使用需求

3.1、配置可匿名訪問的資源

  • 第一步:在項目中創建pages目錄,在pages目錄中創建a.html和b.html
    在這裏插入圖片描述
  • 第二步:在spring-security.xml文件中配置,指定哪些資源可以匿名訪問
<!--
 http:用於定義相關權限控制
 指定哪些資源不需要進行權限校驗,可以使用通配符
 security="none"表示不再進行權限校驗
-->
<security:http security="none" pattern="/pages/a.html" />
<security:http security="none" pattern="/paegs/b.html" />

<!-- 表示pages目錄下所有文件都可以匿名訪問 -->
<security:http security="none" pattern="/pages/**"></security:http>

通過上面的配置可以發現, pages目錄下的文件可以在沒有認證的情況下任意訪問。
在這裏插入圖片描述

3.2、使用指定的登錄頁面

  • 第一步:提供login.html作爲項目的登錄頁面
    login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>指定的登錄頁面</title>
</head>
<style>
    .input{
        height: 40px;
        width: 300px;
        border: 1px solid blue;
        border-radius: 5px;
    }

    input:focus {
        outline: none;
        box-shadow: 0 0 20px mediumspringgreen;
    }

    .button{
        border: 1px solid blue;
        background-color: moccasin;
        color: purple;
        height: 40px;
        width: 60px;
        border-radius: 5px;
    }

</style>

<body>

<div style="text-align: center;padding-top: 50px" >
<form action="/login.do" method="post">
    用戶名:&nbsp;&nbsp;<input class="input"  type="text" name="username"> <br><br>
    密碼:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input class="input" type="password" name="password"><br><br>
   <input class="button" type="submit" value="登錄">
</form>
</div>
</body>

</html>
  • 第二步:修改 spring-security.xml文件,指定login.html頁面可以匿名訪問
<security:http security="none" pattern="/login.html" />
  • 第三步:修改 spring-security.xml文件,加入表單登錄信息的配置
<!--
 form-login:定義表單登錄信息
-->
<security:form-login login-page="/login.html"
          username-parameter="username"
          password-parameter="password"
          login-processing-url="/login.do"
          default-target-url="/index.html"
          authentication-failure-url="/login.html"
          />
<!--
 csrf:對應CsrfFilter過濾器
 disabled:是否啓用CsrfFilter過濾器,如果使用自定義登錄頁面需要關閉此項,否則登錄操作會被禁
用(403)
-->
<security:csrf disabled="true"></security:csrf>

測試:
在這裏插入圖片描述

3.3、從數據庫查詢用戶信息

如果我們要從數據庫動態查詢用戶信息,就必須按照spring security框架的要求提供一個實現
UserDetailsService接口的實現類,並按照框架的要求進行配置。框架會自動調用實現類中的方法
並自動進行密碼校驗。

public class SpringSecurityUserService implements UserDetailsService {
    //    模擬數據庫數據
    public static Map<String, User> map = new HashMap<>();
    static {

        User user1 = new User();
        user1.setUsername("admin");
        user1.setPassword("admin");

        User user2 = new User();
        user2.setUsername("xiaoming");
        user2.setPassword("1234");

        map.put(user1.getUsername(), user1);
        map.put(user2.getUsername(), user2);
    }


    /**
     * 根據用戶名查詢用戶信息
     *
     * @param username 用戶名
     * @return 數據庫用戶信息
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

//       模擬根據用戶名查詢數據庫獲取用戶信息(包含密碼權限等信息)
        User user = map.get(username);
        if (user == null) {
//            用戶名不存在
            return null;
        } else {

            List<GrantedAuthority> list = new ArrayList<>();
//            爲當前用戶授權 (ROLE_開頭 表示角色)
            list.add(new SimpleGrantedAuthority("permission_A")); //授權
            list.add(new SimpleGrantedAuthority("permission_B"));
            list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //授予角色

            //            三個參數  用戶名  數據庫查詢到的密碼  權限List集合
            org.springframework.security.core.userdetails.User scurityUser = new org.springframework.security.core.userdetails.User(username, "{noop}"+user.getPassword(), list);

            // 將用戶信息返回給框架 框架自動進行密碼比對
            return scurityUser;
        }
    }
}

效果:
在這裏插入圖片描述

3.4、對密碼進行加密

前面我們使用的密碼都是明文的,這是非常不安全的。一般情況下用戶的密碼需要進行加密後再保存到
數據庫中。

常見的密碼加密方式有:
3DES、AES、DES:使用對稱加密算法,可以通過解密來還原出原始密碼

MD5、SHA1:使用單向HASH算法,無法通過計算還原出原始密碼,但是可以建立彩虹表進行查表破

bcrypt:將salt隨機並混入最終加密後的密碼,驗證時也無需單獨提供之前的salt,從而無需單獨處理
salt問題(實際項目中常使用這種方式)

加密後的格式一般爲:

$2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa

加密後長度爲固定的60位。其中:$是分隔符,無意義; 2a是bcrypt加密版本號;10是cos的值;後面的前22位是salt值;最後面的字符串就是密碼的密文。

實現步驟:

  • 第一步:在spring-security.xml文件中指定密碼加密對象
    注意 < !–開啓spring註解使用-- >
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/security
        http://www.springframework.org/schema/security/spring-security.xsd">


    <!--開啓spring註解使用-->
    <context:annotation-config></context:annotation-config>


    <!--http:用於定義相關權限控制  指定哪些資源不需要進行權限校驗,可以使用通配符-->

    <!--允許登錄頁面被匿名訪問-->
    <security:http security="none" pattern="/login.html"/>

    <!--<security:http security="none" pattern="/pages/a.html"/>-->
    <!--<security:http security="none" pattern="/paegs/b.html"/>-->

    <!--必須放在最下面-->
    <security:http security="none" pattern="/pages/**"/>


    <!-- auto-config:自動配置,如果設置爲true,表示自動應用一些默認配置,比如框架會提供一個默認的登錄頁面
           use-expressions:是否使用spring security提供的表達式來描述權限 -->
    <security:http auto-config="true" use-expressions="true">

        <!--配置攔截規則,/** 表示攔截所有請求  注意/*只能攔截一層-->
        <!-- pattern:描述攔截規則 asscess:指定所需的訪問角色或者訪問權限-->
        <security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>

        <!--使用自己指定的頁面作爲登錄頁面 必須進行如下配置-->
        <!--form-login:定義表單登錄信息-->
        <!--   login-processing-url="/login.do"  框架攔截該請求進行認證處理-->
        <security:form-login login-page="/login.html"
                             username-parameter="username"
                             password-parameter="password"
                             login-processing-url="/login.do"
                             default-target-url="/index.html"
                             authentication-failure-url="/login.html"
        />
        <!--
         csrf:對應CsrfFilter過濾器
         disabled:是否啓用CsrfFilter過濾器,如果使用自定義登錄頁面需要關閉此項,否則登錄操作會被禁用(403)
        -->
        <security:csrf disabled="true"/>

    </security:http>


    <!--配置認證管理器-->
    <security:authentication-manager>
        <!--配置認證提供者-->
        <security:authentication-provider user-service-ref="userService2">

            <!-- &lt;!&ndash;配置一個具體的用戶,後期需要從數據庫查詢用戶   {noop}1234固定寫法 表示明文密碼1234 &ndash;&gt;
             <security:user-service>
                 <security:user name="admin" password="{noop}1234"
                          authorities="ROLE_ADMIN"/>
                 </security:user-service>-->

            <!--指定對密碼加密的對象-->
            <security:password-encoder ref="passwordEncoder"></security:password-encoder>

        </security:authentication-provider>
    </security:authentication-manager>

    <!--註冊userService 未加密處理-->
    <bean id="userService" class="cn.kinggm520.service.SpringSecurityUserService"></bean>

    <!--加密處理-->
    <bean id="userService2" class="cn.kinggm520.service.SpringSecurityUserService2"></bean>


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

</beans>
  • 第二步:修改 UserService實現類
public class SpringSecurityUserService2 implements UserDetailsService {

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    //    模擬數據庫數據
    public Map<String, User> map = new HashMap<>();

    public void initUserData() {

        User user1 = new User();
        user1.setUsername("admin");

        //使用bcrypt進行加密
        user1.setPassword(passwordEncoder.encode("admin"));

        User user2 = new User();
        user2.setUsername("xiaoming");
        user2.setPassword(passwordEncoder.encode("1234"));

        map.put(user1.getUsername(), user1);
        map.put(user2.getUsername(), user2);


    }

    /**
     * 根據用戶名查詢用戶信息
     *
     * @param username 用戶名
     * @return 數據庫用戶信息
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

//        初始化用戶數據
        this.initUserData();
//       模擬根據用戶名查詢數據庫獲取用戶信息(包含密碼權限等信息)
        User user = map.get(username);
        if (user == null) {
//            用戶名不存在
            return null;
        } else {

            List<GrantedAuthority> list = new ArrayList<>();
//            爲當前用戶授權 (ROLE_開頭 表示角色)
            list.add(new SimpleGrantedAuthority("permission_A")); //授權
            list.add(new SimpleGrantedAuthority("permission_B"));
            list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //授予角色

            //            三個參數  用戶名  數據庫查詢到的密碼  權限List集合
            org.springframework.security.core.userdetails.User scurityUser = new org.springframework.security.core.userdetails.User(username,user.getPassword(), list);

            // 將用戶信息返回給框架 框架自動進行密碼比對
            return scurityUser;

        }
    }
}

效果同上:
Debug查看User裏面的密碼:
可以看到已經加密處理了
在這裏插入圖片描述

3.5、配置多種校驗規則

爲了測試方便,首先在項目中創建a.html、b.html、c.html、d.html幾個頁面
修改spring-security.xml文件:
在 < security:http auto-config>標籤中配置

<!--只要認證通過就可以訪問-->
<security:intercept-url pattern="/index.jsp"  access="isAuthenticated()" />
<security:intercept-url pattern="/a.html"  access="isAuthenticated()" />
<!--擁有add權限就可以訪問b.html頁面-->
<security:intercept-url pattern="/b.html"  access="hasAuthority('add')" />
<!--擁有ROLE_ADMIN角色就可以訪問c.html頁面-->
<security:intercept-url pattern="/c.html"  access="hasRole('ROLE_ADMIN')" />
<!--擁有ROLE_ADMIN角色就可以訪問d.html頁面,
注意:此處雖然寫的是ADMIN角色,框架會自動加上前綴ROLE_-->
<security:intercept-url pattern="/d.html"  access="hasRole('ADMIN')" />
public class SpringSecurityUserService2 implements UserDetailsService {

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;


    //    模擬數據庫數據
    public Map<String, User> map = new HashMap<>();

    public void initUserData() {

        User user1 = new User();
        user1.setUsername("admin");

        //使用bcrypt進行加密
        user1.setPassword(passwordEncoder.encode("admin"));

        User user2 = new User();
        user2.setUsername("xiaoming");
        user2.setPassword(passwordEncoder.encode("1234"));

        map.put(user1.getUsername(), user1);
        map.put(user2.getUsername(), user2);
    }


    /**
     * 根據用戶名查詢用戶信息
     *
     * @param username 用戶名
     * @return 數據庫用戶信息
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        System.out.println();

//        初始化用戶數據
        this.initUserData();
//       模擬根據用戶名查詢數據庫獲取用戶信息(包含密碼權限等信息)
        User user = map.get(username);
        if (user == null) {
//            用戶名不存在
            return null;
        } else {

            List<GrantedAuthority> list = new ArrayList<>();
//            爲當前用戶授權 (ROLE_開頭 表示角色)
            list.add(new SimpleGrantedAuthority("permission_A")); //授權
            list.add(new SimpleGrantedAuthority("permission_B"));

            if("admin".equals(username)){
                list.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //授予角色

//                授予admin add權限 就可以訪問b.html
                list.add(new SimpleGrantedAuthority("add"));
            }

            //            三個參數  用戶名  數據庫查詢到的密碼  權限List集合
            org.springframework.security.core.userdetails.User scurityUser = new org.springframework.security.core.userdetails.User(username,user.getPassword(), list);

            // 將用戶信息返回給框架 框架自動進行密碼比對
            return scurityUser;
        }
    }
}

上面的代碼配置了admin 可以訪問所有的頁面
xiaoming 無法訪問

3.6、 註解方式權限控制

SpringSecurity還可以使用註解方式控制類中方法的調用。例如Controller中的某個方法要求必須具有某個權限纔可以訪問,此時就可以使用SpringSecurity框架提供的註解方式進行控制。
實現步驟:

  • 第一步:在spring-security.xml文件中配置組件掃描,用於掃描Controller
<mvc:annotation-driven></mvc:annotation-driven>
<context:component-scan base-package="com.itheima.controller">
</context:component-scan>
  • 第二步:在 spring-security.xml文件中開啓權限註解支持
<!--開啓註解方式權限控制-->
<security:global-method-security pre-post-annotations="enabled" />
  • 第三步:創建 Controller類並在Controller的方法上加入註解進行權限控制
@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/add")
//    調用此方法要求當前用戶必須具有add權限
    @PreAuthorize("hasAuthority('add')")
    public String add() {
        System.out.println("添加成功!!!");
        return "add successfully!!!";
    }


    //    調用此方法要求當前用戶必須具有ROLE_ADMIN角色
    @RequestMapping("/delete")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String delete() {
        System.out.println("刪除成功!!!");
        return "delete successfully!!!";
    }
}

此時只有具有相應權限的用戶登錄了 才能訪問相關方法

3.7、退出登錄

用戶完成登錄後Spring Security框架會記錄當前用戶認證狀態爲已認證狀態,即表示用戶登錄成功了。
那用戶如何退出登錄呢?我們可以在spring-security.xml文件中進行如下配置:

<!--
 logout:退出登錄
 logout-url:退出登錄操作對應的請求路徑
 logout-success-url:退出登錄後的跳轉頁面
-->
<security:logout logout-url="/logout.do"
        logout-success-url="/login.html" invalidate-session="true"/>

通過上面的配置可以發現,如果用戶要退出登錄,只需要請求 /logout.do這個URL地址就可以,同時會
將當前session失效,最後頁面會跳轉到login.html頁面。實際上框架是基於過濾器實現的退出功能。

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