SpringSecurity的使用


介紹

Spring Security 是一個高度自定義的安全框架。利用 Spring IoC/DI 和 AOP 功能,為系統提供了聲明式安全訪問控制功能,減少了為系 統安全而編寫大量重復代碼的工作。主要實現兩個功能:

  1. 用戶登錄的控制
  2. 登錄后權限的控制

使用

引入依賴

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
 </dependency>

引入了依賴,其實就已經完成了配置,當我請求我們所要進入的頁面時,都會先進行登錄的驗證,才會跳轉到我們訪問的頁面。但是在實際的使用中,我們一般會跳轉到我們請求的頁面做安全的控制以及權限的控制,因此我們需要自定義對應的類。

UserDetailsService接口

當什么也沒有配置的時候,賬號和密碼是由 Spring Security 定義 生成的。而在實際項目中賬號和密碼都是從數據庫中查詢出來的。所 以我們要通過自定義邏輯控制認證邏輯。

三個參數具體解釋:

username:用戶名,這里默認提交表單中的 name 必須叫username

password:密碼

authorities:用戶具有的權限。此處不允許為 null

此處的用戶名應該是客戶端傳遞過來的用戶名。而密碼應該是從 數據庫中查詢出來的密碼。Spring Security 會根據 User 中的 password 和客戶端傳遞過來的 password 進行比較。如果相同則表示認證通過, 如果不相同表示認證失敗。authorities 里面包含的所有內容為此用戶具有的權限,如有里面沒有包含某個權限,而在做 某個事情時必須包含某個權限則會出現 403。通常都是通過 AuthorityUtils.commaSeparatedStringToAuthorityList(“”) 來 創 建 authorities 集合對象的。參數時一個字符串,多個權限使用逗號分隔。

如果需要自定義邏輯時,只需要實現 UserDetailsService 接口即可,就可以進行對應的登錄配置。

BCryptPasswordEncoder

BCryptPasswordEncoder 是 Spring Security 官方推薦的密碼解析器,平時多使用這個解析器。BCryptPasswordEncoder 是對 bcrypt 強散列方法的具體實現。是 基於 Hash 算法實現的單向加密。

encode():把參數按照特定的解析規則進行解析。

matches()驗證從存儲中獲取的編碼密碼與編碼后提交的原始密碼是否匹配。如果密碼匹配,則返回 true;如果不匹配,則返回 false。 第一個參數表示需要被解析的密碼。第二個參數表示存儲的密碼。

//加密的使用
public void test(){
        BCryptPasswordEncoder pe = new BCryptPasswordEncoder();
        String encode = pe.encode("123");//加密
        System.out.println(encode);

        boolean matches = pe.matches("1234", encode);//原數據和加密匹配
        System.out.println(matches);//false
    }

自定義登錄邏輯

當進行自定義登錄邏輯時需要用到之前講解的 UserDetailsService 和 PasswordEncoder。但是 Spring Security 要求:當進行自定義登錄邏輯時容器內必須有 PasswordEncoder 實例。因此通過創建配置類注入該對象

  • PasswordEncoder配置類
@Configuration
public class SecurityConfig{
 @Bean
    public PasswordEncoder getPe() {
        return new BCryptPasswordEncoder();
    }
}
  • 編寫登錄用戶邏輯,需要實現UserDetailsService
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder encoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.查詢數據庫,查詢用戶名是否存在,如果不存在拋出UsernameNotFoundException
        if (!username.equals("admin")){
            throw new  UsernameNotFoundException("用戶名不存在");
        }
        //2.把查詢出來的密碼進行解析,或直接把構造方法放到構造方法中
        //password 就是數據庫中查詢出來的密碼,查詢內容不是 123
        String password = encoder.encode("123");//加密后的數據,這里是模擬數據庫中的密碼
      //三個參數:用戶名,數據庫加密后的密碼,實現的權限,角色,可訪問的頁面
        return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_adminN,/main.html,/main1.html"));
    }
}

自定義頁面邏輯

簡單說就是,你要設置那個頁面做主頁,那些頁面不用驗證,那些頁面需要認證,那個角色或者權限能訪問那個頁面

該。配置類需要繼承 WebSecurityConfigurerAdapte,並重寫 configure 方法。

  • 常用的配置中調用的方法

    successForwardUrl()登錄成功后跳轉地址,使用 successForwardUrl()時表示成功后轉發請求到地址。內部是通過 successHandler()方法進行控制成功后交給哪個類進行處理。ForwardAuthenticationSuccessHandler 內部就是最簡單的請求轉發。由於是請求轉發,當遇到需要跳轉到站外或在前后端分離的項目中就無法使用了。

    當需要控制登錄成功后去做一些事情時,可以進行自定義認證成功控制器。這里以成功控制器為例,自定義認證失敗控制器只需要繼承AuthenticationFailureHandler即可

    //自定義登錄成功處理器
    public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        private String url;
    
        public MyAuthenticationSuccessHandler(String url) {
            this.url = url;
        }
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
            System.out.println(httpServletRequest.getRemoteAddr());
            User user = (User) authentication.getPrincipal();
            System.out.println(user.getPassword());//默認 null
            System.out.println(user.getAuthorities());//權限
            httpServletResponse.sendRedirect(url);
        }
    }
    

    loginPage() 登錄頁面

    loginProcessingUrl 登錄頁面表單提交地址,此地址可以不真實存在。

    antMatchers():匹配內容 permitAll():允許

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private MyAccessDeniedHandler myAccessDeniedHandler;
    
        @Autowired
        private UserDetailServiceImpl userDetailService;
    
        @Autowired
        private DataSource dataSource;
    
        @Autowired
        private PersistentTokenRepository repository;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            System.out.println("執行UserDetailServiceImpl");
            //表單認證(登錄)
            http.formLogin()
                    .loginProcessingUrl("/login")//當發現/login時認為是登錄,需要執行UserDetailServiceImpl
                     .successForwardUrl("/toMain")//此處是一個 post 請求不能寫.html 頁面,靜態資源只能走 get 請求
                    //自定義跳轉
                   // .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
                    .failureHandler(new MyAuthenticationFailHandler("/fail.html"))
                    //.failureForwardUrl("/fail")
                    .loginPage("/showLogin");//登錄頁面
    
    
            //url 攔截(授權)
            http.authorizeRequests()
                    .antMatchers("/showLogin", "/fail.html").permitAll()//login.html 不需要被認證
                    // .mvcMatchers("/login.html").servletPath("/hello").permitAll()
                   // .antMatchers("/main1.html").hasIpAddress("0:0:0:0:0:0:0:1")//具有其中一個就能訪問
                    //.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");//所有的請求都必須被認證,也就是必須登錄后才能訪問
                    .anyRequest().authenticated();
    
            //關閉 csrf
           // http.csrf().disable();
    
            //異常
            http.exceptionHandling()
                    .accessDeniedHandler(myAccessDeniedHandler);
    
            http.rememberMe()
                    //.tokenValiditySeconds()設置有效時間默認 2 周
                    .userDetailsService(userDetailService)//用戶登錄邏輯寫在那個對象中
                    .tokenRepository(repository);
    
            //退出
            http.logout()
                    .logoutSuccessUrl("/showLogin");
        }
    
    
        @Bean
        public PersistentTokenRepository getPer(){
            JdbcTokenRepositoryImpl jt = new JdbcTokenRepositoryImpl();
            jt.setDataSource(dataSource);//需要一個數據源
            //jt.setCreateTableOnStartup(true);//第一次需要使用
            return jt;
        }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM