從零開始SpringCloud Alibaba電商系統(七)——Spring Security實現登錄認證、權限控制

零、系列

歡迎來嫖從零開始SpringCloud Alibaba電商系列:

  1. 從零開始SpringCloud Alibaba電商系統(一)——Alibaba與Nacos服務註冊與發現
  2. 從零開始SpringCloud Alibaba電商系統(二)——Nacos配置中心
  3. 從零開始SpringCloud Alibaba電商系統(三)——Sentinel流量防衛兵介紹、流量控制demo
  4. 從零開始SpringCloud Alibaba電商系統(四)——Sentinel的fallback和blockHandler
  5. 從零開始SpringCloud Alibaba電商系統(五)——Feign Demo,Sentinel+Feign實現多節點間熔斷/服務降級
  6. 從零開始SpringCloud Alibaba電商系統(六)——Sentinel規則持久化到Nacos配置中心

一、Spring Security是什麼?

Spring Security is a powerful and highly customizable authentication and access-control framework. ————spring官網

顯然而易見,它是一個可以幫我們實現登錄認證、角色(資源)權限控制的框架,此外,它還提供了一些諸如CSRF攻擊攔截的功能,有興趣的同學可以查一下。

有的同學可能疑惑,權限認證這種東西我寫個select+ifelse不就完了嗎?用它幹啥?
嗯,看完,然後,真香……

二、登錄認證

登錄認證的演示將會做大概如下幾個步驟:

  • 配置spring security相關內容。
  • 簡單敘述用戶角色權限。
  • 代碼配置。
  • 代碼用戶邏輯編寫。
  1. 我們繼續在之前的demo基礎上開發(新建一個springboot項目也可以,無甚區別)。
<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  1. 增加一個WebSecurityConfigurerAdapter的繼承類,我們可以通過這個類配置如何驗證用戶對那些路徑進行驗證,對請求加入哪些攔截器校驗等。
    實際上,當我們沒有任何配置的時候,Spring Security會幫我們自動攔截所有請求並跳轉一個由該框架提供的默認登錄界面,如下圖:
package com.lele.mall.config;/*
 * com.lele.mall.config
 * @author: lele
 * @date: 2020-04-18
 */

import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configurable
@EnableWebSecurity
public class SimpleSecurityConfig extends WebSecurityConfigurerAdapter {

   
}

在這裏插入圖片描述
3. WebSecurityConfigurerAdapter提供了幾種配置。這裏我配置一些簡單的規則:除了/test之外所有url進行認證,可認證用戶名密碼只有(admin,admin)。

重寫configure(HttpSecurity http)可以配置`對那些路徑進行驗證`。
重寫 configure(AuthenticationManagerBuilder auth)可以配置`如何驗證用戶`。

在這裏插入圖片描述
然後嘗試訪問localhost:8081/order/apply,結果直接跳轉到登錄頁面:
在這裏插入圖片描述
4. 配置我們的用戶,我在這裏寫了死數據,正常項目我們一般配合數據庫來做。
值得一提,這裏需要我們自己實現一個UserDetailsService的實現類並重載它的loadUserByUsername方法。它的作用顯而易見:給它一個username,它返回一個完整的User對象
修改後的SimpleSecurityConfig如下:

package com.lele.mall.config;/*
 * com.lele.mall.config
 * @author: lele
 * @date: 2020-04-18
 */

import com.lele.mall.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configurable
@EnableWebSecurity
public class SimpleSecurityConfig extends WebSecurityConfigurerAdapter {

    // 引入自定義userDetails
    @Autowired
    MyUserDetailsService myUserDetailsService;

    @Bean
    public PasswordEncoder passwordEncoder(){
        //return new BCryptPasswordEncoder();
        return NoOpPasswordEncoder.getInstance(); // 不對密碼進行加密
    }

    /**
     * 通過http對象配置具體的認證規則、路由權限規則
     *  這裏用http對象代替xm配置,注意這裏每個and()之間的配置都相當於原來xml中一個標籤包含的配置。
     * @param http
     * @throws Exception
     */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .formLogin()// 使用默認的表單登錄
                    .permitAll()
                    .and()
                .authorizeRequests()
                    .antMatchers("/test")
                    .permitAll()
                    .anyRequest() // 捕獲所有路徑
                    .authenticated();
        }

    /**
     * 用戶認證規則,即用戶傳遞何種信息纔可以登錄
     *  myUserDetailsService.loadUserByUsername 通過傳入username,返回我們的user數據,
     *  passwordEncoder 會對user數據中的password進行BCrypt算法加密。
     * @param auth
     * @throws Exception
     */
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(myUserDetailsService);
            //.passwordEncoder(passwordEncoder());
        }

}

新增MyUserDetail如下:

package com.lele.mall;/*
 * com.lele.mall
 * @author: lele
 * @date: 2020-04-19
 */

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;


@Service
public class MyUserDetailsService implements UserDetailsService {

    /**
     * 這裏應該實現自定義認證邏輯:通過username查庫找到user
     * @param s
     * @return 返回一個Srping Security規定的UserDetails,包含密碼和用戶權限
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        if( !s.equals("admin") )
            throw new UsernameNotFoundException(s + "用戶名不存在");

        // 資源權限,之後可以通過這裏賦予的權限控制接口訪問。
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();
        roles.add(new SimpleGrantedAuthority( "ROLE_ADMIN"));
        return new User(s,"admin",roles);
    }
}
  1. 測試訪問,第一次使用admin,pass訪問,失敗。第二次使用admin,admin訪問,成功!可以正常訪問接口。

在這裏插入圖片描述在這裏插入圖片描述

以上我們介紹了一個spring security最簡單的一個例子,之後在我們即將構建的電商系統中,還會介紹如何配置一個自己的登錄頁面、如何對密碼加密解密以及CSRF攻擊攔截等功能。

三、訪問權限控制

  之前我們介紹了登錄認證的基本用法,接下來我們將介紹,基於用戶、角色權限,如何實現對接口的訪問控制。
  1. 開啓權限認證,在Application頭頂上配置@EnableGlobalMethodSecurity註解prePostEnabled爲true,此開關不開,後面的訪問控制盡皆無效。
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MallApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(MallApplication.class, args);
    }
}
  1. 在order/apply方法腦袋上配置註解@PreAuthorize(“hasRole(‘ROLE_ADMIN’)”),代表擁有‘ROLE_ADMIN’角色才能請求該方法,ROLE_ADMIN角色我們已經在之前的UserDetails中賦予了admin用戶。
    注意:這裏的角色系統中也需要我們自行設計,這裏只是用默認的方便展示。

  2. 登錄admin,並訪問 /order/apply,訪問成功!想看失敗結果的同學可以將UserDetails中賦予角色的代碼去掉,會發現此時訪問會出現403錯誤。

在這裏插入圖片描述

四、demo地址

https://github.com/flyChineseBoy/lel-mall/tree/master/mall07

下一節,我們繼續來看Spring Security,它在分佈式場景下,是如何保證各個節點間登錄信息互通的。

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