SpringSecurity 學習總結

一些基礎的知識能夠參考如下鏈接:https://www.cnkirito.moe/spring-security-1/  講真,這老哥寫的很好,不過還有不少估計沒寫完,遺憾spring

本文章主要是一個大概的總結。安全

首先SpringSecurity是一個安全框架,它所作的事兒簡單歸納就是使用Filter對整個項目進行一個保護,框架

保護的內容就包括 認證 和 受權驗證 若是單針對Filter的話 他包括 登錄認證,和對URL的權限驗證, 登錄認證是在SpringSecurity的過濾器鏈中的AbstractAuthenticationProcessingFilter的子類來作的 具體步驟ide

請求進來->若干前置過濾器->AbstractAuthenticationProcessingFilter.doFilter()-> AbstractAuthenticationProcessingFilter.attemptAuthentication()->若是獲取到了Authentication則繼續往下執行,不然重定向到登錄頁面.... 從attemptAuthentication方法中的認證操做是調用了AuthenticationManager的authenticate方法來進行登錄認證,而後會把用戶提交的用戶信息封裝成AuthenticationToken的實例丟進去,在其中進行驗證,驗證成功則返回填充了相關信息的Token對象,不然是null,則說明認證失敗,會重定向到咱們設置好的loginUrl。ui

這個認證過程當中還涉及到 AuthenticationManager 的認證 是 AuthenticationManager的一個實現類ProviderManager,而ProviderManager會有一個List專門存放Provider,這些Provider都要實現AuthenticationProvider接口,只要有一個Provider支持放進來的Authentication則會由這個Provider進行處理,且這個方法能夠拋出異常,拋出也說明登錄失敗。this

因此若是要自定義本身的一套登錄認證的話,url

咱們涉及到三個地方 Filter - Provider - Token (例子採用上面連接的老哥的例子)code

咱們須要自定義本身的過濾器 經過 繼承 AbstractAuthenticationProcessingFilter 類 實現 attemptAuthentication方法orm

public class IpAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

    public IpAuthenticationProcessingFilter(String uri) {
        super(new AntPathRequestMatcher(uri));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
        //獲取host信息
        String host = httpServletRequest.getRemoteHost();
        //交給內部的AuthenticationManager去認證,實現解耦
        return getAuthenticationManager().authenticate(new IpAuthenticationToken(host));
    }
}

咱們須要自定義本身的認證提供器 經過繼承 AuthenticationProvider 類對象

public class IpAuthenticationProvider implements AuthenticationProvider {
    final static Map<String, SimpleGrantedAuthority> ipAuthorityMap = new ConcurrentHashMap<>();
    //維護一個ip白名單列表,每一個ip對應必定的權限
    static {
        ipAuthorityMap.put("127.0.0.1", new SimpleGrantedAuthority("ADMIN"));
        ipAuthorityMap.put("10.0.0.8", new SimpleGrantedAuthority("FRIEND"));
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (authentication instanceof IpAuthenticationToken){
            String ip = ((IpAuthenticationToken)authentication).getIp();
            if (ipAuthorityMap.containsKey(ip)){
                HashSet<SimpleGrantedAuthority> grants = new HashSet();
                grants.add(ipAuthorityMap.get(ip));
                return new IpAuthenticationToken(ip,grants);
            }
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return IpAuthenticationToken.class.isAssignableFrom(aClass);
    }
}

咱們須要自定義本身的 認證對象 通常就是咱們的領域對象 可是這個對象必須實現 Authentication 接口 咱們能夠直接繼AbstractAuthenticationToken類便可

public class IpAuthenticationToken extends AbstractAuthenticationToken {
    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    private String ip;

    public IpAuthenticationToken(String ip) {
        super(null);
        this.ip = ip;
        super.setAuthenticated(false);
    }

    public IpAuthenticationToken(String ip, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.ip = ip;
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}

而後咱們須要將provider添加到ProviderManager的List中去

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(new IpAuthenticationProvider());
    auth.userDetailsService(userDetailsService).passwordEncoder(NoOpPasswordEncoder.getInstance());
}

 將咱們自定義的Filter註冊到過濾器鏈中去

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/iplogin","/login").permitAll()
            .anyRequest().authenticated()
            .and()
        .formLogin()
        .loginPage("/iplogin");
    //這個操做就是把咱們自定義的過濾器插入到UsernamePasswordAuthenticationFilter的前面,表示優先使用咱們的驗證,若是驗證成功則會將Authentication對象放入                    //SecurityContext中,後面的過濾器在進行驗證時會檢測SecurityContext中是否有相應的對象,沒有則會
    http.addFilterBefore(ipAuthenticationProcessingFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}

搞定 這個時候咱們的登錄驗證方式已經改爲了iplogin(雖然原始的login還在,可是因爲咱們設置了loginPage的url爲/iplogin因此那個頁面在登錄失敗的時候永遠也訪問不到,除非你直接訪問/login....)

相關文章
相關標籤/搜索