首頁>技術>
序言

前面我們學習了 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

平淡無奇的啟動類:

@SpringBootApplicationpublic 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 模擬資料庫查詢,這裡做了簡化。

@Servicepublic 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) 啟用方法級別的安全校驗。

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 */@Servicepublic 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 */@Servicepublic 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 */@RestControllerpublic 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 PageThis application has no explicit mapping for /error, so you are seeing this as a fallback.Tue Jan 12 23:26:31 CST 2021There was an unexpected error (type=Forbidden, status=403).Forbidden

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

小結

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

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

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

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

27
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 10分鐘快速搭建小程式管理後臺,雲開發CMS搭建視覺化後臺