spring security 整合 springboot 入門案例

2021-03-02 老馬嘯西風
序言

前面我們學習了 spring security 與 springmvc 的整合入門教程。

這一節我們來學習一下 spring security 與 springboot 整合,為了力求簡單,此處不演示資料庫相關操作。

快速開始pom.xml

引入核心的 spring-boot-starter-security 依賴

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>2.1.5.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

目錄結構

整體目錄結構如下:

│  Application.java

├─config
│      MyPasswordEncoder.java
│      WebSecurityConfig.java

├─controller
│      AuthController.java

├─model
│      UserInfo.java

└─service
        MyUserDetailsService.java
        UserInfoService.java

Application.java

平淡無奇的啟動類:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }


}

UserInfo & UserInfoService

UserInfo 對應資料庫表中的基本用戶信息,如下

public class UserInfo {

    /**
     * 用戶名
     */
    private String username;

    /**
     * 密碼
     */
    private String password;

    /**
     * 角色列表
     */
    private List<String> roles;

    //Getter & Settter
}

UserInfoService 模擬資料庫查詢,這裡做了簡化。

@Service
public class UserInfoService {

    /**
     * 查詢用戶信息
     * 1. 移除資料庫交互,簡單實現。
     * @param username 用戶名稱
     * @return 結果
     */
    public UserInfo queryUserInfo(final String username) {
        UserInfo userInfo = new UserInfo();
        if("user".equals(username) || "admin".equals(username)) {
            userInfo.setUsername(username);
            // 密碼可以在入庫的時候就進行加密
            userInfo.setPassword("123456");
            // 角色需要以 ROLE_ 開頭
            userInfo.setRoles(Arrays.asList("ROLE_" + username));
            return userInfo;
        }

        throw new UsernameNotFoundException(username+"對應信息不存在");
    }

}

ps: ROLE_ 這個前綴主要是為了後面使用角色註解授權的時候需要,默認的前綴就是這個。

WebSecurityConfig.java  核心配置類

這個類就是最核心的配置類了。

啪的一下,很快啊。

我們上來就是用了兩個註解,@EnableWebSecurity 啟用 web 安全,@EnableGlobalMethodSecurity(prePostEnabled = true) 啟用方法級別的安全校驗。

import com.github.houbb.spring.security.learn.springboot.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author 老馬嘯西風
 * @since 1.0.0
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 開啟方法級安全驗證
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;

    @Autowired
    private MyPasswordEncoder myPasswordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService)
            .passwordEncoder(myPasswordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated() // 所有請求都需要驗證
                .and()
                .formLogin().permitAll() // 使用默認的登錄頁面,登錄頁面允許所有用戶訪問
                .and()
                .csrf().disable();// post請求要關閉csrf驗證,不然訪問報錯;實際開發中開啟,需要前端配合傳遞其他參數
    }

}

一般情況下,MyUserDetailsService 和 MyPasswordEncoder 這兩個類都是需要我們自定義的。

MyUserDetailsService 用戶信息查詢

我們只需要實現 UserDetailsService 接口,就可以實現對應的查詢實現。

這裡的授權信息,直接使用 SimpleGrantedAuthority 類。

import com.github.houbb.spring.security.learn.springboot.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

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

/**
 * 自定義根據名稱獲取用戶信息的實現
 *
 * @author binbin.hou
 * @since 1.0.0
 */
@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserInfoService userInfoService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserInfo userInfo = userInfoService.queryUserInfo(username);

        // 授權信息構建
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : userInfo.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role));
        }

        return new User(
                userInfo.getUsername(),
                userInfo.getPassword(),
                authorities
        );
    }

}

MyPasswordEncoder 密碼加密策略

spring security 有很多內置的加密策略,這裡為了演示,我自定義了最簡單的 plainText 的策略,就是不做任何加密。

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * 密碼加密策略
 * @author binbin.hou
 * @since 1.0.0
 */
@Service
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {
        return (String) rawPassword;
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return rawPassword.equals(encodedPassword);
    }
}

AuthController 控制器

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
@RestController
public class AuthController {

    /**
     * 查看登錄用戶信息
     */
    @GetMapping("/auth")
    public Authentication auth(){
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 只能 user 角色才能訪問該方法
     * @return 結果
     */
    @PreAuthorize("hasAnyRole('user')")
    @GetMapping("/user")
    public String user(){
        return "user角色訪問";
    }

    /**
     * 只能 admin 角色才能訪問該方法
     * @return 結果
     */
    @PreAuthorize("hasAnyRole('admin')")
    @GetMapping("/admin")
    public String admin(){
        return "admin角色訪問";
    }

}

這裡我們定義了 3 個方法,第一個方法是獲取當前用戶的登錄信息。

後面兩個方法都是通過 @PreAuthorize 指定訪問需要的角色信息。

測試驗證登錄

看到這裡的小夥伴也許會問,你怎麼不寫 login 對應實現呢?

實際上 springboot 把默認的 login/logout 都做了封裝,我們平時學習可以直接使用。

如果是真實生產,一般需要自己寫。

我們啟動應用,瀏覽器訪問 http://localhost:8080/auth 想查看登錄信息,因為所有請求都需要登錄驗證,所以會被重定向到登錄頁面

http://localhost:8080/login

輸入信息

我們輸入 admin/123456 以 admin 的角色登錄。

則可以獲取到授權信息如下:

{"authorities":[{"authority":"ROLE_admin"}],"details":{"remoteAddress":"127.0.0.1","sessionId":"8871ED88F86B4CD67EAA2FBAC40C68C2"},"authenticated":true,"principal":{"password":null,"username":"admin","authorities":[{"authority":"ROLE_admin"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null,"name":"admin"}

角色授權測試

我們訪問 http://localhost:8080/admin,頁面返回

admin角色訪問

我們訪問 http://localhost:8080/user,頁面返回

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Jan 12 23:26:31 CST 2021
There was an unexpected error (type=Forbidden, status=403).
Forbidden

也就是 403 權限不足,訪問被拒絕。

小結

一個最簡單的 spring security 與 springboot 整合就這樣搞定了,是不是特別簡單呢?

類比 shiro,spring security 肯定也是在登錄的時候為我們做了相關的密碼驗證+授權信息保存,通過攔截器對請求進行攔截校驗。

不得不說,spring 的封裝確實優秀,後面我們進一步深入的學習,做到熟練地使用 spring security。

希望本文對你有所幫助,如果喜歡,歡迎點讚收藏轉發一波。

我是老馬,期待與你的下次相遇。

相關焦點

  • 超全的springboot+springsecurity實現前後端分離簡單實現!
    通過各種方式學習springsecurity,在B站、騰訊課堂、網易課堂、慕課網沒有springsecurity的前後端分離的教學視頻,那我就去csdn去尋找springsecurity博客,發現幾個問題:  實在不行我又跑去github上找開源項目學習,github由於是外國網站,國內訪問速度有點慢!!
  • springboot+springsecurity實現前後端分離簡單實現!
    通過各種方式學習springsecurity,在B站、騰訊課堂、網易課堂、慕課網沒有springsecurity的前後端分離的教學視頻,那我就去csdn去尋找springsecurity博客,發現幾個問題:要麼就是前後端不分離,要麼就是通過內存方式讀取數據,而不是通過資料庫的方式讀取數據,要麼就是大佬們給的代碼不全、把代碼講的太繞,關鍵部分沒有注釋
  • 「SpringSecurity-1」Springboot+Security+Oauth2授權碼模式
    話不多說開始搞……項目搭建項目中使用到的相關框架的版本號:springboot版本:2.3.0.RELEASEspring-security-oauth2版本:2.3.3.RELEASE創建認證伺服器添加依賴pom.xml中添加依賴,特別說明這是一個父子項目,父pom中添加了spring-boot-dependencies
  • 「SpringSecurity-3」Springboot+Security + Oauth2授權碼模式
    授權登錄頁面自定義實現授權登陸頁修改第一步修改security配置文件。獲取授權碼時修改登錄頁面,需要對授權伺服器配置進行修改,修改如下:第二步添加login.htmlpom.xml文件中引入 spring-boot-starter-thymeleaf 依賴在resources文件夾下創建static文件夾並創建login.html文件login.html文件如下:這樣我們就自定義了一個登陸頁面,並且將登陸頁面集成到
  • SpringSecurity + JWT,從入門到精通!
    在前後端分離的項目中,你知道怎麼Spring security整合JWT麼,來看看這篇文章哈!思維導圖如下:org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
  • SpringBoot整合Spring Security
    這篇文章就是想通過一個小案例將Spring Security整合到SpringBoot中去。要實現的功能就是在認證伺服器上登錄,然後獲取Token,再訪問資源伺服器中的資源。基本概念單點登錄什麼叫做單點登錄呢。
  • springboot整合shiro 框架
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達66套java從入門到精通實戰課程分享所以自己整合了一個比較簡單的shiro的使用方法,shiro是什麼、怎麼用就不用我在這裡詳細的講解了。;import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager
  • SpringBoot(五) :spring data jpa 的使用
    (點擊上方公眾號,可快速關注)來源:純潔的微笑,www.ityouknow.com/springboot/2016/
  • springboot整合redis簡單案例
    前言:這裡簡單介紹下springboot整合redis(window版),以及簡單的測試demo。根據當前自己電腦的系統下載對應的redis版本,我這裡是以window版本作為測試的 demo。下載完直接解壓到自定義的目錄,如下:雙擊運行redis-server.exe,如下說明redis啟動成功:在創建的springboot項目的配置文件中,配置如下:創建配置類:
  • 學習學習SpringSecurity
    ><artifactId>spring-boot-starter-security</artifactId></dependency>重新部署,會看到一個登陸頁面。;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
  • Spring Boot Security 詳解
    </groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId>
  • 船新「漲薪秘籍」阿里巴巴內部爆款頂配版Spring Security筆記
    由於它是Spring生態系統中的一員,因此它伴隨著整個Spring生態系統不斷修正、升級,在spring boot項目中加入springsecurity更是十分簡單,使用Spring Security 減少了為企業系統安全控制編寫大量重複代碼的工作。
  • springboot(五):spring data jpa的使用
    在上篇文章springboot(二):web綜合開發中簡單介紹了一下spring data jpa的基礎性使用,這篇文章將更加全面的介紹spring
  • SpringBoot+ Dubbo + Mybatis + Nacos +Seata整合來實現Dubbo分布式事務
    1.簡介「本文主要介紹SpringBoot2.1.5 + Dubbo 2.7.3 + Mybatis 3.4.2 + Nacos 1.1.3 +Seata 0.8.0整合來實現Dubbo分布式事務管理,使用Nacos 作為 Dubbo和Seata的註冊中心和配置中心,使用 MySQL 資料庫和 MyBatis來操作數據。
  • SpringBoot-Condition
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達  作者 |  那棵tree看起來生氣了來源 |  urlify.cn/7J3aqq66套java從入門到精通實戰課程分享springboot中提供了一系列@Condition* 註解來處理有條件注入的情況。1. 說明Spring4中增加了@Condition annotation, 使用該Annotation之後,在做依賴注入的時候,會檢測是否滿足某個條件來決定是否注入某個類。
  • SpringBoot註解@ControllerAdvice知多少?
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達66套java從入門到精通實戰課程分享配置spring 版本:<org.springframework-version>4.1.9.RELEASE</org.springframework-version>  spring-servlet.xml,注意必須開啟註解,即xml
  • Springboot整合easyExcel導入導出Excel
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達  作者 |  Code2020來源 |  urlify.cn/BFnIrq背景:最近公司有個需求要求可以導入、導出excel,因此在此記錄學習一下如何使用Springboot整合
  • pacebox-springboot 1.0.8 發布,java 生態框架
    pacebox-springboot 融合封裝已發布,旨在提供快速開發腳手架、打造更好的開源生態環境。
  • springboot整合mybatis實現配置多數據源
    一 項目結構:二 涉及到的資料庫:三 springboot中的application.properties文件:本次demo簡單配置下兩個數據源為例。上邊db1和db2可自行定義,只要是下邊自定義數據源配置文件需要用到,需要注意的是springboot2.x配置多數據源時,url要改成jdbc-url注意的是:四 自定義數據源配置文件:下邊以數據源db1為例,數據源db2內容一樣,關鍵@Bean區分下,以及spring.datasource.XX區分下即可五 單元測試:
  • SpringBoot整合EasyPoi實現Excel的導入和導出(帶圖片)
    點擊上方藍色字體,選擇「標星公眾號」優質文章,第一時間送達66套java從入門到精通實戰課程分享EasyPoi文檔:http://easypoi.mydoc.io/項目地址:https://gitee.com/lihongmin5211/springboot_easypoi準備工作環境:SpringBoot2.4.0、jdk8、Mysql5.7表結構: