SpringSercurity基本原理

spring sercurity 基礎


環境配置: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命名空間配置登陸相關信息的標籤,它包含以下屬性:

  1. login-page 自定義登陸頁url,默認爲/login
  2. login-processing-url 登陸請求攔截的url,也就是form表單提交時指定的action
  3. default-target-url 默認登陸成功後跳轉的url
  4. always-use-default-target 是否老是使用默認的登陸成功後跳轉url
  5. authentication-failure-url 登陸失敗後跳轉的url
  6. username-parameter 用戶名的請求字段 默認爲userName
  7. password-parameter 密碼的請求字段 默認爲password
  8. authentication-success-handler-ref 指向一個AuthenticationSuccessHandler用於處理認證成功的請求,不能和default-target-url還有always-use-default-target同時使用
  9. authentication-success-forward-url 用於authentication-failure-handler-ref
  10. authentication-failure-handler-ref 指向一個AuthenticationFailureHandler用於處理失敗的認證請求
  11. authentication-failure-forward-url 用於authentication-failure-handler-ref
  12. authentication-details-source-ref 指向一個AuthenticationDetailsSource,在認證過濾器中使用

自定義登陸界面

  1. 設置 上述屬性login-page 在請求須要登陸認證的api,發現用戶沒有通過登陸認證,spring security 會自動跳轉到配置的login-page的路由(方法,url,controller...)
  2. 若是是rest api 服務(先後端分離),不須要返回html頁面,能夠在指定的 login-page 直接返回json格式的響應
  3. 若是是須要返回html頁面,也能夠在 login-page 的controller返回頁面
  4. login-page 的controller對請求做出判斷,選擇不一樣的返回方式便可

自定義登陸成功處理

先後端分離的狀況下,登陸請求,客戶端須要的是一個rest的返回結果,而不是跳轉url,這就須要咱們去作一些自定義的配置

  1. 建立一個類實現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));
    }
}
  1. 在spring security config類裏面配置上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 星期六

相關文章
相關標籤/搜索