環境配置:html
win10java
idea 2019.1git
jdk1.8spring
spring cloud Finchley.RELEASE數據庫
spring-boot 2.0.2.RELEASEjson
git && gitee.com後端
一系列的過濾器組成的鏈路.以下圖:api
FilterSercurityInterceptor就是spring security處理鑑權的入口,在訪問REST api 前都會通過這個過濾器,若是經過了鑑權纔會跳轉到對應api的入口app
下面來分析在默認開啓spring security的狀況下各類場景的一些執行路徑,經過流程圖展現出來;前後端分離
對應操做: 打斷點在FilterSecurityInterceptor.doFilter()
上,便可追蹤其執行路徑
未登陸,請求REST api
已登陸,請求REST api,權限不夠
已登陸,請求REST api,權限足夠
須要繼承WebSecurityConfigurerAdapter
,並重寫其configure()
方法.
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 說明: 重寫security的配置方法 * @author suwenguang * @date 2019/6/7 * @return void <- 返回類型 */ @Override protected void configure(HttpSecurity http) throws Exception { //配置 http.formLogin() .and() .authorizeRequests() .anyRequest() .authenticated(); } }
實現UserDetailsService
接口
@Component @Slf4j public class UserCertificationService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.info("用戶名:{}", username); log.info("密碼:{}",passwordEncoder.encode("123456")); //根據username到數據庫獲取用戶信息 return new User(username, passwordEncoder.encode("123456") ,true,true,true,true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user")); } }
利用UserDetails
UserDetails
是個接口,能夠實現等方式建立本身系統的子類,以達到擴展的效果.固然也可使用自帶的. org.springframework.security.core.userdetails.User
UserDetails內部提供以下的四種狀態
isAccountNonExpired
isAccountNonLocked
isCredentialsNonExpired
isCredentialsNonExpired
PasswordEncoder
配置一個PasswordEncoder
新建PasswordEncoderConfig.java
@Configuration public class PasswordEncoderConfig { /** * 說明: 注入一個明文密碼的加解密 * @author suwenguang * @date 2019/6/7 * @return org.springframework.security.crypto.password.PasswordEncoder <- 返回類型 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
form-login屬性詳解
form-login是spring security命名空間配置登陸相關信息的標籤,它包含以下屬性:
login-page
自定義登陸頁url,默認爲/login
login-processing-url
登陸請求攔截的url,也就是form表單提交時指定的actiondefault-target-url
默認登陸成功後跳轉的urlalways-use-default-target
是否老是使用默認的登陸成功後跳轉url
authentication-failure-url
登陸失敗後跳轉的urlusername-parameter
用戶名的請求字段 默認爲userName
password-parameter
密碼的請求字段 默認爲password
authentication-success-handler-ref
指向一個AuthenticationSuccessHandler
用於處理認證成功的請求,不能和default-target-url
還有always-use-default-target
同時使用authentication-success-forward-url
用於authentication-failure-handler-ref
authentication-failure-handler-ref
指向一個AuthenticationFailureHandler
用於處理失敗的認證請求authentication-failure-forward-url
用於authentication-failure-handler-ref
authentication-details-source-ref
指向一個AuthenticationDetailsSource
,在認證過濾器中使用login-page
在請求須要登陸認證的api,發現用戶沒有通過登陸認證,spring security 會自動跳轉到配置的login-page
的路由(方法,url,controller...)login-page
直接返回json格式的響應login-page
的controller返回頁面login-page
的controller對請求做出判斷,選擇不一樣的返回方式便可先後端分離的狀況下,登陸請求,客戶端須要的是一個rest的返回結果,而不是跳轉url,這就須要咱們去作一些自定義的配置
AuthenticationSuccessHandler
接口,實現onAuthenticationSuccess()
方法,把Authentication
信息包裝成json返回.新建SystemAuthenticationSuccessHandler.java
@Component @Slf4j public class SystemAuthenticationSuccessHandler implements AuthenticationSuccessHandler { /** * 說明:登陸成功後,執行的方法 * @author suwenguang * @date 2019/6/8 * @return void <- 返回類型 */ @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info("登陸成功:{}", JSON.toJSONString(authentication)); response.setContentType("application/json;charset=UTF-8"); PrintWriter writer = response.getWriter(); Response res = ResponseBuilder.build(ResponseEnums.SIMPLE_SUCCESS, authentication); writer.write(JSON.toJSONString(res)); } }
successHandler()
,把剛剛的類做爲參數傳入便可修改SecurityConfig.java
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private SystemAuthenticationSuccessHandler systemAuthenticationSuccessHandler; /** * 說明: 重寫security的配置方法 * @author suwenguang * @date 2019/6/7 * @return void <- 返回類型 */ @Override protected void configure(HttpSecurity http) throws Exception { //配置 http.formLogin() //自定義登陸方法 .loginPage("/login.html") .loginProcessingUrl("/login") //自定義登陸認證成功處理器 .successHandler(systemAuthenticationSuccessHandler) .and() .authorizeRequests() //配置不須要認證的路由 .antMatchers("/login.html","**static**").permitAll() //全部路由都要認證 .anyRequest().authenticated() //禁用跨站攻擊防禦機制 .and().csrf().disable() ; } }
道理同上,可是此次須要實現的接口變成了AuthenticationFailureHandler
,其餘步驟幾乎同上
下面貼上處理的代碼
新建SystemAuthenticationFailHandler.java
@Component @Slf4j public class SystemAuthenticationFailHandler implements AuthenticationFailureHandler { /** * 說明: 認證失敗後,執行的方法 * @author suwenguang * @date 2019/6/8 * @return void <- 返回類型 */ @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("登陸失敗:{}", exception.getMessage()); response.setContentType("application/json;charset=UTF-8"); PrintWriter writer = response.getWriter(); Response res = ResponseBuilder.build(ResponseEnums.LOGIN_FAIL, exception.getMessage()); writer.write(JSON.toJSONString(res)); } }
修改SecurityConfig.java
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 登陸成功的處理器 **/ @Autowired private SystemAuthenticationSuccessHandler systemAuthenticationSuccessHandler; /** * 登陸失敗的處理器 **/ @Autowired private SystemAuthenticationFailHandler systemAuthenticationFailHandler; /** * 說明: 重寫security的配置方法 * @author suwenguang * @date 2019/6/7 * @return void <- 返回類型 */ @Override protected void configure(HttpSecurity http) throws Exception { //配置 http.formLogin() //自定義登陸方法 .loginPage("/login.html") .loginProcessingUrl("/login") //自定義登陸認證成功處理器 .successHandler(systemAuthenticationSuccessHandler) //自定義登陸失敗處理器 .failureHandler(systemAuthenticationFailHandler) .and() .authorizeRequests() //配置不須要認證的路由 .antMatchers("/login.html","**static**").permitAll() //全部路由都要認證 .anyRequest().authenticated() //禁用跨站攻擊防禦機制 .and().csrf().disable() ; } }
2019-06-08 20:54:47 星期六