Spring boot整合security詳解

前言

在進行框架選型時最常用的選擇就是在Spring security 和Shiro中進行抉擇,Spring security 和 shiro 一樣,都具有認證、授權、加密等用於權限管理的功能。但是對於Springboot而言,Spring Security比Shiro更合適一些,他們都是Spring生態裡的內容,並且在使用上Spring boot隻需要引入Security就可以實現基礎的登陸驗證。

配置依賴

spring boot的依賴版本:2.7.1

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.1</version>
		<relativePath/> 
	</parent>

添加Security的依賴版本為:2.6.7

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
	<version>2.6.7</version>
</dependency>

這樣就簡單集成瞭security瞭,現在啟動項目進行訪問會直接有瞭登陸頁面

這個是security進行瞭簡單登陸的實現,官方提供的默認賬號是user,密碼會在啟動命令臺裡打印,下圖中的即是密碼

這個密碼每次啟動都會隨機生成,也可以在配置文件中進行指定,在配置文件中加入一下代碼

spring:
  security:
    user:
      name: admin
      password: 123456
      roles: admin

再重啟項目,此時的賬號密碼就是設置的這個瞭,當然光這樣做肯定不滿足我們的權限需求,下面實現我們的具體權限配置

用戶配置

​ 要實現自定義配置,首先創建一個繼承於WebSecurityConfigurerAdapter的配置類,並且實現configure方法,這個方法裡就是自定義實現權限的邏輯

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
    }
}

@EnableWebSecurity註解,這個註解是Spring Security用於啟用web安全的註解。

​ Spring Security的配置用戶存儲地址有四種實現方式

  1. 內存用戶存儲
  2. 數據庫用戶存儲
  3. LDAP用戶存儲
  4. 自定義用戶存儲

1.內存用戶存儲

這個存儲方式就是寫死在程序瞭,啟動的時候初始化好瞭用戶權限的集合,優點是很快,因為基於內存,缺點是不靈活、無法動態更改權限、不可以進行註冊操作,所以我們基本不用這種方式。

重寫configure方法後啟動查看效果,在登陸頁面可以通過這兩個賬號進行登陸就完成瞭

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().passwordEncoder(passwordEncoder())
                .withUser("admin").password(passwordEncoder().encode("123456")).authorities("ADMIN")
                .and()
                .withUser("anduoduo").password(passwordEncoder().encode("123456")).authorities("ORDINARY");
    }
    private PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

2.數據庫用戶存儲

用戶權限放在數據庫中是我們最常用的方式,這樣可以讓我們可以很方便地對用戶信息進行增刪改查。並且還可以為用戶添加除認證信息外的附加信息。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
                .usersByUsernameQuery(
                        "select username, password, status from Users where username = ?")
                .authoritiesByUsernameQuery(
                        "select username, authority from Authority where username = ?");
    }
    private PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

auth調用jdbcAuthentication()來告訴Spring Security使用jdbc的方式來查詢用戶和權限,dataSource()方法指定數據庫連接信息,passwordEncoder()指定密碼加密規則,用戶的密碼數據應該以同樣的方式進行加密存儲,不然,兩個加密方式不同的密碼,匹配補上。usersByUsernameQuery()和authoritiesByUsernameQuery()方法分別定義瞭查詢用戶和權限信息的sql語句。

3.LDAP用戶存儲

這種方式很少見應該,LDAP是一個文件協議,這種方式就是通過獲取文件來做權限內容,之前log4j出現的被侵入bug就是因為這一部分,可以通過LDAP進行代碼執行。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> configurer =                     auth.ldapAuthentication()
                .userSearchBase("ou=people")
                .userSearchFilter("(uid={0})")
                .groupSearchBase("ou=groups")
                .groupSearchFilter("member={0}");
        configurer.passwordCompare()
                .passwordEncoder(passwordEncoder())
                .passwordAttribute("passcode");
        configurer.contextSource().url("ldap://xxxxx.com:17099/dc=xxxxxx,dc=com");
    }
    private PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

4.自定義用戶存儲

​ 自定義用戶存儲,就是自行使用認證名稱來查找對應的用戶數據,然後交給Spring Security使用。這種方式需要一個實現UserDetailsService的service類,

public class UserServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.getUserByUsername(username);
        return user == null ? new User() : user;
    }
}

這個實現類隻需要實現一個方法:loadUserByUsername()。該方法需要做的是使用傳過來的username來匹配一個帶有密碼等信息的用戶實體。User的實體類需要實現UserDetails,因為返回對象是UserDetails,也就是說,查到的信息裡,必須得有Spring Security所需要的信息。

public class User implements UserDetails {
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    @Override
    public String getPassword() {
        return null;
    }
    @Override
    public String getUsername() {
        return null;
    }
    @Override
    public boolean isAccountNonExpired() {
        return false;
    }
    @Override
    public boolean isAccountNonLocked() {
        return false;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }
    @Override
    public boolean isEnabled() {
        return false;
    }
}

config代碼

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder());
    }
    @Bean
    private PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

這個配置隻需要告訴Spring Security的UserDetailsService實現類是哪個就可以瞭,它會去調用loadUserByUsername()來查找用戶。

攔截配置

攔截也是同樣在SecurityConfig配置類裡的configure方法,隻不過重載瞭configure方法

// 配置安全策略
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 設置路徑及要求的權限,支持 ant 風格路徑寫法
        http.authorizeRequests()
          		// 設置 OPTIONS 嘗試請求直接通過
            	.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            	.antMatchers("/api/demo/user").hasAnyRole("user", "admin")
            	// 註意使用 hasAnyAuthority 角色需要以 ROLE_ 開頭
                .antMatchers("/api/demo/admin").hasAnyAuthority("ROLE_admin")
                .antMatchers("/api/demo/hello").permitAll()
                .and()
            	// 開啟表單登錄
                .formLogin().permitAll()
                .and()
            	// 開啟註銷
                .logout().permitAll();
    }

到此這篇關於Spring boot整合security詳解的文章就介紹到這瞭,更多相關Spring boot security內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: