springSecurity初步認識和執行流程

springSecurity是spring官方給咱們提供的一個很是強大的一個安全框架。也是如今最受歡迎的安全框架,比shiro更強大html

 

springSecurity主要工做原理是內置了許多過濾器,組成過濾器鏈,每一個過濾器都有本身的明確分工,而後還有異常處理類,還有最後的一個認證受權類。看圖java

綠色的是表明過濾器鏈,咱們能夠本身配置增長和刪除,藍色的是異常處理類,後面黃色的是最後的認證處理類,這兩個類的位置是不會變的,因此說,最終咱們的請求會到filterSecurityInterceptor這個類中來判斷到底能不能訪問咱們請求的url   web

好比說:咱們直接一個請求(不是登錄請求),他先會到filterSecurityInterceptor這個類,而後驗證,沒有成功就會拋出異常,讓ExceptionTranslationFiter這個類來處理(實際上就是一個轉發到登陸頁面),而後填上帳號密碼,登錄,會讓usernamepasswordAuthenticationFilter這個類攔截,而後最後仍是在filterSecurityInterceptor這個類來判斷是否要對咱們所請求的url作請求算法

 

看下面的一個簡單demospring

BrowserSecurityConfig.java數據庫

package com.imooc.security.browser;

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

/**
 * Created by 敲代碼的卡卡羅特
 * on 2018/4/15 21:48.
 */
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.httpBasic()   //彈出框請求
        http.formLogin()   //表單驗證
                .and()
                .authorizeRequests() //開啓認證請求
                .anyRequest()        //任何請求
                .authenticated();    //都須要認證
    }
}
View Code

聲明一個配置文件,配置最基本的功能。apache

而後咱們再訪問url就須要登錄了,原始的用戶名是user,密碼是框架系統生成了驗證碼,咱們須要改變一下。用咱們自定義的帳號和密碼。json

  MyUserDetailService.java    (最終返回一個User對象)   自定義用戶很簡單就是@Component 這個類,讓spring管理就行
 
package com.imooc.security.browser;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * Created by 敲代碼的卡卡羅特
 * on 2018/4/15 22:08.
 */
@Component
public class MyUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        //、、、、、、省略根據用戶名從數據庫獲得user對象的過程  這個方法就是根據登錄的用戶名而後返回一個user對象,這個user對象是框架提供給咱們的
        //繼承了UserDetails這個類。咱們也能夠自定義類繼承UserDetails
        return new User(s,"123", AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
    }
}
View Code

 

 

 UserDetails.java   源碼解析緩存

public interface UserDetails extends Serializable {
    //權限集合
    Collection<? extends GrantedAuthority> getAuthorities();
    //密碼
    String getPassword();
    //帳號
    String getUsername();
    //帳號是否過時
    boolean isAccountNonExpired();
    //帳號是否鎖定
    boolean isAccountNonLocked();
    //密碼是否過時
    boolean isCredentialsNonExpired();
    //帳號是否可用
    boolean isEnabled();
}
View Code

 

 密碼--》很簡單,只須要在配置文件配置一下這個bean就行, BCryptPasswordEncoder這個類是框架給咱們提供的。安全

可是咱們在日常用的時候確定密碼存的明文,因此咱們須要一個加密算法,咱們來配一個加密類。

@Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
View Code

 

 

 

若是咱們還想更加定製化一些呢?

好比說:登錄成功去那個方法,登錄失敗去那一個方法。爲了方便記錄日誌嘛。固然。這些能夠在配置文件中配置,很是簡單。看代碼

自定義成功和失敗的處理類,只須要new一個類實現框架給的特定的接口就行。而後在配置文件中配置

ImoocAuthenticationSuccessHandler.java   //成功處理類
/**
 * 
 */
package com.imooc.security.browser.authentication;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.security.core.properties.LoginResponseType;
import com.imooc.security.core.properties.SecurityProperties;

/**
 * @author zhailiang
 *
 */
@Component("imoocAuthenticationSuccessHandler")
public class ImoocAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private ObjectMapper objectMapper;  //springboot默認注入了這個類,處理json的

    @Autowired
    private SecurityProperties securityProperties; //自定義的參數類,不用管。就是配置的url之類的。

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.security.web.authentication.
     * AuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.
     * HttpServletRequest, javax.servlet.http.HttpServletResponse,
     * org.springframework.security.core.Authentication)
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        // 注意authentication這個參數可牛了   這裏面封裝了你當前對象的全部信息
        logger.info("登陸成功");
        //這是業務邏輯,不用管,就是說若是設置了json格式,就返回前臺json,若是不是就返回頁面
        if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        } else {
            //調用父類的方法,其實就是跳轉到原來的頁面
            super.onAuthenticationSuccess(request, response, authentication);
        }

    }

}
View Code
onAuthenticationFailure.java            //失敗處理類
/**
 * 
 */
package com.imooc.security.browser.authentication;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.imooc.security.browser.support.SimpleResponse;
import com.imooc.security.core.properties.LoginResponseType;
import com.imooc.security.core.properties.SecurityProperties;

/**
 * @author zhailiang
 *
 */
@Component("imoocAuthenctiationFailureHandler")
public class ImoocAuthenctiationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    private Logger logger = LoggerFactory.getLogger(getClass());
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @Autowired
    private SecurityProperties securityProperties;

    
    /* (non-Javadoc)
     * @see org.springframework.security.web.authentication.AuthenticationFailureHandler#onAuthenticationFailure(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.AuthenticationException)
     */
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        
        logger.info("登陸失敗");
        
        if (LoginResponseType.JSON.equals(securityProperties.getBrowser().getLoginType())) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage())));
        }else{
            super.onAuthenticationFailure(request, response, exception);
        }
        
        
    }

}
View Code

 

 
BrowserSecurityConfig.java     //配置文件
package com.imooc.security.browser;

import com.imooc.security.browser.authentication.ImoocAuthenctiationFailureHandler;
import com.imooc.security.browser.authentication.ImoocAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * Created by 敲代碼的卡卡羅特
 * on 2018/4/15 21:48.
 */
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    ImoocAuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
    @Autowired
    ImoocAuthenctiationFailureHandler imoocAuthenctiationFailureHandler;


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.httpBasic()   //彈出框請求
        http.formLogin()   //表單驗證
                .loginPage("/authentication/require") //返回登陸頁,能夠是一個action,也能夠是一個html頁面
                .loginProcessingUrl("/authentication/form") //點擊登錄的action,默認是login 咱們能夠自定義
                .successHandler(imoocAuthenticationSuccessHandler) //配置登錄成功的處理類
                .failureHandler(imoocAuthenctiationFailureHandler)//配置登錄失敗的處理類
                .and()
                .authorizeRequests() //開啓認證請求
                .antMatchers("/imooc-signIn.html","/authentication/require").permitAll()//忽略認證
                .anyRequest()        //任何請求
                .authenticated();    //都須要認證

        http.csrf().disable();   //關閉csrf

    }
}
View Code

 


自定義未登錄跳轉接口 (也就是進一步細化判斷,寫日誌什麼的,日常咱們在配置文件中配的就是跳轉到登錄頁面,這是跳轉到接口)
package com.imooc.security.browser;

import com.imooc.security.browser.support.SimpleResponse;
import com.imooc.security.core.properties.SecurityConstants;
import com.imooc.security.core.properties.SecurityProperties;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * Created by 敲代碼的卡卡羅特
 * on 2018/4/16 0:16.
 */
@RestController
public class BrowserSecurityController {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private RequestCache requestCache = new HttpSessionRequestCache();  //提供用緩存中獲得url的類
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); //提供跳轉的類



    @Autowired
    private SecurityProperties securityProperties;
    /**
     * 當須要身份認證時,跳轉到這裏
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED) //返回錯誤狀態碼
    public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        SavedRequest savedRequest = requestCache.getRequest(request, response);

        if (savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            logger.info("引起跳轉的請求是:" + targetUrl);
            if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
            }
        }

        return new SimpleResponse("訪問的服務須要身份認證,請引導用戶到登陸頁");
    }

}
View Code

 

最後:咱們能夠在代碼的任何地方拿到當前登錄對象的信息(首先是已經登陸) 有三種方法

@GetMapping("/user1")
    public Object getUser1() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
    @GetMapping("/user2")
    public Object getUser2(Authentication authentication) {
        return authentication;
    }
    @GetMapping("/user3")
    public Object getUser3(@AuthenticationPrincipal UserDetails user) {
        return user;
    }
View Code

 實現記住我功能  ,配置很簡單,業務邏輯是,你配置好框架會給你在你數據庫中建立一個表,包含用戶名,token,時間等字段,而後也會在你本地存個cookie,登錄完成在表中插入一條數據,若是下次登錄的話,驗證你信息,若是沒有攜帶認證信息,最後會查你的數據庫,而後獲得你用戶名,而後再調那個根據用戶名獲得用戶的方法認證成功。看代碼

@Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private SecurityProperties securityProperties;
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); //new一個這個類
        tokenRepository.setDataSource(dataSource); //指定數據源,由於要存數據庫
        tokenRepository.setCreateTableOnStartup(true);//在啓動的時候建立表
        return tokenRepository;
    }

 @Override
    protected void configure(HttpSecurity http) throws Exception {
//        http.httpBasic()   //彈出框請求
        http.formLogin()   //表單驗證
                .loginPage("/authentication/require") //返回登陸頁,能夠是一個action,也能夠是一個html頁面
                .loginProcessingUrl("/authentication/form") //點擊登錄的action,默認是login 咱們能夠自定義
                .successHandler(imoocAuthenticationSuccessHandler) //配置登錄成功的處理類
                .failureHandler(imoocAuthenctiationFailureHandler)//配置登錄失敗的處理類
                .and()
                .rememberMe() //記住我
                .tokenRepository(persistentTokenRepository())  //指定TokenRepository的bean
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())//過時時間
                .userDetailsService(userDetailsService)//指定根據名字獲得用戶的類
                .and()
                .authorizeRequests() //開啓認證請求
                .antMatchers("/imooc-signIn.html","/authentication/require").permitAll()//忽略認證
                .anyRequest()        //任何請求
                .authenticated();    //都須要認證

        http.csrf().disable();   //關閉csrf

    }
View Code

 

 

最後:前臺傳過來的必定是  name="remember-me"  這個字段

相關文章
相關標籤/搜索