spring security簡單教程以及實現徹底先後端分離

spring security是spring家族的一個安全框架,入門簡單。對比shiro,它自帶登陸頁面,自動完成登陸操做。權限過濾時支持http方法過濾。html

在新手入門使用時,只須要簡單的配置,便可實現登陸以及權限的管理,無需本身寫功能邏輯代碼。前端

可是對於如今大部分先後端分離的web程序,尤爲是前端廣泛使用ajax請求時,spring security自帶的登陸系統就有一些不知足需求了。java

由於spring security有本身默認的登陸頁,本身默認的登陸控制器。而登陸成功或失敗,都會返回一個302跳轉。登陸成功跳轉到主頁,失敗跳轉到登陸頁。若是未認證直接訪問也會跳轉到登陸頁。可是若是前端使用ajax請求,ajax是沒法處理302請求的。先後端分離web中,規範是使用json交互。咱們但願登陸成功或者失敗都會返回一個json。何況spring security自帶的登陸頁太醜了,咱們仍是須要使用本身的。git

 

spring security通常簡單使用:github

web的安全控制通常分爲兩個部分,一個是認證,一個是受權。web

認證管理:ajax

就是認證是否爲合法用戶,簡單的說是登陸。通常爲匹對用戶名和密碼,即認證成功。spring

在spring security認證中,咱們須要注意的是:哪一個類表示用戶?哪一個屬性表示用戶名?哪一個屬性表示密碼?怎麼經過用戶名取到對應的用戶?密碼的驗證方式是什麼?數據庫

只要告訴spring security這幾個東西,基本上就能夠了。編程

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

}

事實上只要繼承WebSecurityConfigurerAdapter ,spring security就已經啓用了,當你訪問資源時,它就會跳轉到它本身默認的登陸頁。可是這還不行,

當用戶點擊登陸時,

1.它會拿到用戶輸入的用戶名密碼;

2.根據用戶名經過UserDetailsService 的 loadUserByUsername(username)方法得到一個用戶對象;

3.得到一個UserDetails 對象,得到內部的成員屬性password;

4.經過PasswordEncoder 的 matchs(s1, s2) 方法對比用戶的輸入的密碼和第3步的密碼;

5.匹配成功;

 

因此咱們要實現這三個接口的三個方法:

1.實現UserDetailsService ,能夠選擇同時實現用戶的正常業務方法和UserDetailsService ;

例如:UserServiceImpl implement IUserService,UserDetailsService {}

2.實現UserDetails ,通常使用用戶的實體類實現此接口。

其中有getUsername(), getPassword(), getAuthorities()爲獲取用戶名,密碼,權限。可根據我的狀況實現。

3.實現PasswordEncoder ,spring security 提供了多個該接口的實現類,可百度和查看源碼理解,也能夠本身寫。

三個實現類的配置以下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

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

其中Userdetails 爲UserDetailsService 中 loadUserByUsername() 方法的返回值類型。

 

到目前爲止,就能夠完成簡單認證了。而受權管理,到如今,是默認的:全部資源都只有‘認證’權限,全部用戶也只有‘認證’權限。即,通過認證就能夠訪問全部資源。

以上,就是spring security的簡易應用。能夠實現一個稍微完整的安全控制。很是簡單。

 

受權管理:

受權管理,是在已認證的前提下。用戶在認證後,根據用戶的不一樣權限,開放不一樣的資源。

根據RBAC設計,用戶有多個角色,角色有多個權限。(真正控制資源的是權限,角色只是一個權限列表,方便使用。)

每一個用戶都有一個權限列表,受權管理,就是權限和資源的映射。在編程中,寫好對應關係。而後當用戶請求資源時,查詢用戶是否有資源對應的權限決定是否經過。

權限寫在數據庫,配置文件或其餘任何地方。只要調用loadUserByUsername()時返回的UserDetails對象中的getAuthorities()方法能獲取到。

因此不管用戶的權限寫在哪裏,只要getAuthorities()能獲得就能夠了。

舉例:

受權管理映射:add==/api/add,query==/api/query;

數據庫中存儲了用戶權限:query;

那麼該用戶就只能訪問/api/query,而不能訪問/api/add。

受權管理配置以下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/data").hasAuthority("add")
                .antMatchers(HttpMethod.GET, "/api/data").hasAuthority("query")
                .antMatchers("/home").hasAuthority("base");
    }
}

以上就是spring security的基本應用。下面是解決先後端分離下的沒法302跳轉的狀況。

需求是:先後端分離,須要本身的登陸頁面,使用ajax請求。

出現問題:本身的登陸頁面請求登陸後,後端返回302跳轉主頁,ajax沒法處理;未認證請求資源時,後端返回302跳轉登陸頁,也沒法處理。

解決思想:修改302狀態碼,修改成401,403或者200和json數據。

 

HttpSecurity 有不少方法,能夠看一看

好比 設置登陸頁(formLogin().loginPage("/login.html")) 能夠設置本身的登陸頁(該設置主要是針對使用302跳轉,且有本身的登陸頁,若是不使用302跳轉,先後端徹底分離,無需設置)。

好比 設置認證成功處理

好比 設置認證失敗處理

好比 設置異常處理

好比 設置退出成功處理

能夠繼承重寫其中的主要方法(裏面有httpResponse對象,能夠隨便返回任何東西)

例如:

import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.setStatus(HttpStatus.OK.value());
    }
}

設置完成登陸成功和失敗處理後,仍是不夠知足需求,當用戶未經過登陸頁進入網站,咱們須要在用戶直接訪問資源時,告訴前端此用戶未認證。(默認是302跳轉到登陸頁)。咱們能夠改爲返回403狀態碼。

這裏就須要實現一個特殊的方法:AuthenticationEntryPoint 接口的 commence()方法。

這個方法主要是,用戶未認證訪問資源時,所作的處理。

spring security給咱們提供了不少現成的AuthenticationEntryPoint 實現類,

好比默認的302跳轉登陸頁,好比返回403狀態碼,還好比返回json數據等等。固然也能夠本身寫。和上面的登陸處理同樣,實現接口方法,將實現類實例傳到配置方法(推薦spring注入)。

以下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Qualifier("userService")
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Autowired
    private MyLogoutHandler logoutHandler;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .formLogin()
                .loginProcessingUrl("/login")
                // 登陸成功
                .successHandler(loginSuccessHandler)
                // 登陸失敗
                .failureHandler(loginFailureHandler).permitAll()
                .and()
                // 註銷成功
                .logout().logoutSuccessHandler(logoutHandler)
                .and()
                // 未登陸請求資源
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint())
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/api/data").hasAuthority("add")
                .antMatchers(HttpMethod.GET, "/api/data").hasAuthority("query")
                .antMatchers("/home").hasAuthority("base");
    }
}

以上就算是完了,前端發起ajax請求時,後端會返回200,401,403狀態碼,前端可根據狀態碼作相應的處理。

如下是個人所有代碼(後端,安全管理demo)

https://github.com/Question7/spring-security-demo

相關文章
相關標籤/搜索