前言
發現很少關於spring security的文章,基本都是入門級的,配個UserServiceDetails或者配個路由控制就完事了,而且很多還是xml配置,國內通病...so,本文裡的配置都是java配置,不涉及xml配置,事實上我也不會xml配置
spring security的大體介紹
spring security本身如果只是說配置,還是很簡單易懂的(我也不知道網上說spring security難,難在哪裡),簡單不需要特別的功能,一個WebSecurityConfigurerAdapter的實現,然後實現UserServiceDetails就是簡單的數據庫驗證了,這個我就不說了。
spring security大體上是由一堆Filter(所以才能在spring mvc前攔截請求)實現的,Filter有幾個,登出Filter(LogoutFilter),用戶名密碼驗證Filter(UsernamePasswordAuthenticationFilter)之類的,Filter再交由其他組件完成細分的功能,例如最常用的UsernamePasswordAuthenticationFilter會持有一個AuthenticationManager引用,AuthenticationManager顧名思義,驗證管理器,負責驗證的,但AuthenticationManager本身並不做具體的驗證工作,AuthenticationManager持有一個AuthenticationProvider集合,AuthenticationProvider才是做驗證工作的組件,AuthenticationManager和AuthenticationProvider的工作機制可以大概看一下這兩個的java doc,然後成功失敗都有相對應該Handler 。大體的spring security的驗證工作流程就是這樣了。
開始配置多AuthenticationProvider
首先,寫一個內存認證的AuthenticationProvider,這裡我簡單地寫一個只有root帳號的AuthenticationProvider
package com.scau.equipment.config.common.security.provider;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Created by Administrator on 2017-05-10.
*/
@Component
public class InMemoryAuthenticationProvider implements AuthenticationProvider {
private final String adminName = "root";
private final String adminPassword = "root";
//根用戶擁有全部的權限
private final List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("CAN_SEARCH"),
new SimpleGrantedAuthority("CAN_SEARCH"),
new SimpleGrantedAuthority("CAN_EXPORT"),
new SimpleGrantedAuthority("CAN_IMPORT"),
new SimpleGrantedAuthority("CAN_BORROW"),
new SimpleGrantedAuthority("CAN_RETURN"),
new SimpleGrantedAuthority("CAN_REPAIR"),
new SimpleGrantedAuthority("CAN_DISCARD"),
new SimpleGrantedAuthority("CAN_EMPOWERMENT"),
new SimpleGrantedAuthority("CAN_BREED"));
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(isMatch(authentication)){
User user = new User(authentication.getName(),authentication.getCredentials().toString(),authorities);
return new UsernamePasswordAuthenticationToken(user,authentication.getCredentials(),authorities);
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
private boolean isMatch(Authentication authentication){
if(authentication.getName().equals(adminName)&&authentication.getCredentials().equals(adminPassword))
return true;
else
return false;
}
}
support方法檢查authentication的類型是不是這個AuthenticationProvider支持的,這裡我簡單地返回true,就是所有都支持,這裡所說的authentication為什麼會有多個類型,是因為多個AuthenticationProvider可以返回不同的Authentication。
public Authentication authenticate(Authentication authentication) throws AuthenticationException 方法就是驗證過程。
如果AuthenticationProvider返回了null,AuthenticationManager會交給下一個支持authentication類型的AuthenticationProvider處理。
另外需要一個數據庫認證的AuthenticationProvider,我們可以直接用spring security提供的DaoAuthenticationProvider,設置一下UserServiceDetails和PasswordEncoder就可以了
@Bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
daoAuthenticationProvider.setUserDetailsService(userServiceDetails);
return daoAuthenticationProvider;
}
最後在WebSecurityConfigurerAdapter裡配置一個含有以上兩個AuthenticationProvider的AuthenticationManager,依然重用spring security提供的ProviderManager
package com.scau.equipment.config.common.security;
import com.scau.equipment.config.common.security.handler.AjaxLoginFailureHandler;
import com.scau.equipment.config.common.security.handler.AjaxLoginSuccessHandler;
import com.scau.equipment.config.common.security.provider.InMemoryAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.UserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Arrays;
import java.util.List;
/**
* Created by Administrator on 2017/2/17.
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userServiceDetails;
@Autowired
InMemoryAuthenticationProvider inMemoryAuthenticationProvider;
@Bean
DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
daoAuthenticationProvider.setUserDetailsService(userServiceDetails);
return daoAuthenticationProvider;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.rememberMe().alwaysRemember(true).tokenValiditySeconds(86400).and()
.authorizeRequests()
.antMatchers("/","/*swagger*/**", "/v2/api-docs").permitAll()
.anyRequest().authenticated().and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/login")
.successHandler(new AjaxLoginSuccessHandler())
.failureHandler(new AjaxLoginFailureHandler()).and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/");
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/public/**", "/webjars/**", "/v2/**", "/swagger**");
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
ProviderManager authenticationManager = new ProviderManager(Arrays.asList(inMemoryAuthenticationProvider,daoAuthenticationProvider()));
//不擦除認證密碼,擦除會導致TokenBasedRememberMeServices因為找不到Credentials再調用UserDetailsService而拋出UsernameNotFoundException
authenticationManager.setEraseCredentialsAfterAuthentication(false);
return authenticationManager;
}
/**
* 這裡需要提供UserDetailsService的原因是RememberMeServices需要用到
* @return
*/
@Override
protected UserDetailsService userDetailsService() {
return userServiceDetails;
}
}
基本上都是重用了原有的類,很多都是默認使用的,只不過為了修改下行為而重新配置。其實如果偷懶,直接用一個UserDetailsService,在裡面做各種認證也是可以的~不過這樣就沒意思了
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持。