Spring Security的使用 #Web安全#權限認證《Spring實戰》第4版筆記 (一)

9.1 Spring Security簡介

Spring Security 是基於Spring AOP和Servlet規範中的Filter實現的安全框架,能夠在Web請求級別和方法調用級別處理身份認證和授權。

Spring Security 從兩個角度來解決安全性問題。它使用Servlet 規範中的Filter的保護Web請求並限制URL級別的訪問。Spring Security 通過使用Spring AOP 保護方法調用——藉助於對象代理和使用通知,能夠確保只有具備適當權限的用戶才能訪問安全保護的方法。

9.1.1 Speing Security的模塊

9.1.2 過濾Web請求

通過web.xml或者AbstractSecurityWebApplicationInitializer的子類配置 DelegatingFilterProxy,都會攔截髮往應用中的請求,並將請求委託給ID爲 springSecurityFilterChain 的bean。

9.1.3 簡單的安全性配置

@Configuration
@EnableWebSecurity  // ->啓用web應用的安全性功能
public class SecurityConfig extends WebSecurityConfigurerAdapter{
}

使用SpringMVC開發,則用 @EnableWebMvcSecurity替代@EnableWebSecurity,通過重載 WebSecurityConfigurerAdapter的是哪個configure()方法來配置Web安全性。

方法 描述
configure(WebSecurity) 通過重載,配置Spring Security的Filter鏈
configure(HttpSecurity) 通過重載,配置如何通過攔截器保護請求
configure(AuthenticationManagerBuilder) 通過重載,配置user-detail服務

默認的configure(HttpSecurity) 代碼

prorected void configure(HttpSecurity http) throws Exception{
   http
     .authrizeRequests()
       .anyRequest().authenicated()  // 所有進入應用的HTTP請求都要驗證
       .and()
     .formLogin().add() // 支持基於表單的登錄以及HTTP Basic方式的認證
     .httpBasic();  
}
//單沒有用戶存儲支撐認證過程,沒有用戶存儲,實際上就等於沒有用戶,沒人能登錄成功
  • 配置用戶存儲
  • 指定哪些請求需要認證,哪些請求不需要認證,以及所需要的權限;
  • 提供一個自定義的登錄界面,替代原來簡單的默認登錄頁。

9.2 選擇查詢用戶信息的服務

9.2.1 基於內存的用戶存儲

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.inMemoryAuthentication() //啓用內存用戶存儲
                .withUser("user").password("password").roles("USER").and()
                .withUser("admin").password("password").roles("USER","ADMIN");
    }
}

配置用戶詳細信息的方法

方法 描述
accountExpired(boolean) 定義賬號是否已經過期
accountLocked(boolean) 定義賬號是否已經鎖定
and() 用來連接配置
authorities(GrantedAuthority…) 授予某個用戶一項或多項權限
authorities(List<? extendsGrantedAuthority…> ) 授予某個用戶一項或多項權限
authorities(String…) 授予某個用戶一項或多項權限
credentialsExpired(boolean) 定義憑證是否已經過期
disabled(boolean) 定義賬號是否已被禁用
password(String) 定義用戶的密碼
roles(String…) 授予某個用戶一項或多項角色

以上方法在學習實現過程中,發現了已經有幾個地方出現了更改
1.@EnbaleWebMvcSecurity已被啓用 -> 改爲@EnableWebSecurity
2.執行測試之後出現異常

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
	at org.springframework.security.crypto.password.DelegatingPasswordEncoder$UnmappedIdPasswordEncoder.matches(DelegatingPasswordEncoder.java:250) ~[spring-security-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.security.crypto.password.DelegatingPasswordEncoder.matches(DelegatingPasswordEncoder.java:198) ~[spring-security-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
	at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$LazyPasswordEncoder.matches(WebSecurityConfigurerAdapter.java:592) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]

測試成功的代碼

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) //啓用內存用戶存儲
                .withUser("user").password(new BCryptPasswordEncoder().encode("password")).roles("USER").and()
                .withUser("admin").password(new BCryptPasswordEncoder().encode("password")).roles("USER","ADMIN");
    }
}

9.2.2 基於數據庫表進行認證

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    DataSource dataSource;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth
                .jdbcAuthentication()
                .dataSource(dataSource)
                .usersByUsernameQuery(
                        "select user_id,user_pwd,true from t_user where user_id=?" 
                )
                .authoritiesByUsernameQuery(
                        "select user_id,'ROLE_USER' from t_user where user_id=?"
                )
                .passwordEncoder(NoOpPasswordEncoder.getInstance());
               //.passwordEncoder(new BCryptPasswordEncoder());
    }
}

注意 passwordEncoder( )這個方法,Spinrg實戰第4版給訊息:Spring Security的加密模塊包括了三個這樣的實現:BCryptPasswordEncoder、NoOpPasswordEncoder和StandardPasswordEncoder,也可以自己實現特定的加密解密算法。
不管使用那一個密碼轉碼器,都需要劣跡一點,數據庫中的密碼是永遠不會解碼的。所採取的的策略與之相反,用戶在登錄時輸入的密碼會按照相同的算法進行轉碼,然後再與數據庫中已經轉碼過的密碼進行對比。

簡單的說就是 如果你數據庫中存的是明文密碼,即假設123456,那是不需要這個屬性的,使用NoOpPasswordEncoder!但是這個“無”加碼器與後面的那個標準解碼器在新版本的Spring Security已經被棄用了,但是出於安全考慮的啓用,官方推薦不要使用明文存儲密碼 。

所以要麼使用啓用的No··Encoder 要麼在數據庫存儲的數據就採用算法進行加密

9.2.3 基於LDAP進行認證

9.2.4 配置自定義的服務

如果內置的用戶存儲無法通用認證需求時,纔有配置自定義的必要,假設用戶存儲在NoSQL中

public class SpitterUserService implements UserDetailsService {

    @Autowired
    private SpitterRepository spitterRepository;  //注入

    @Override
    public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException {
        Spitter spitter=spitterRepository.findByUserId(userId);  //查找Spitter
        if(spitter!=null){
            List<GrantedAuthority> authorities=new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER")); //創建權限列表
            return new User(   //返回User
                spitter.getUserId(),
                spitter.getUserPwd(),
                authorities);
            }
        throw new UserIdNotFoundException();
        }
    }
}
@Autowired
SpitterRepository spitterRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throw Exception{
   auth.userDetailsService(new SpitterUserService(spitterRepository));
}

通過實現UserDetailsService 可以不管用戶數據在哪,只會查找Spitter對象,甚至可以僞造一個,也不用關心底層所使用的數據存儲,只是獲得Spitter對象,並用它來創建User(User是UserDetails的具體實現)

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