SpringSecurity--認證的配置以及debug流程跟蹤

第四章 認證

使用數據庫保存/查詢用戶數據,完成認證功能

4.1 方式一:重寫jdbcAuthentication規則(不推薦)

  1. 基於數據庫的RBAC查詢出我們需要的用戶以及這些用戶的權限(權限標識、角色)
  2. 創建和SpringSecurity要求一模一樣的表,然後用默認jdbcAuthentication
  3. 更新jdbcAuthentication裏面所有我們需要實際運行的sql
  4. authoritiesByUsernameQuery:根據用戶名查詢他權限的sql
  5. usersByUsernameQuery:根據用戶名查詢用戶的sql
  6. .......:更多的sql均可定義

4.1.1 使用默認的查詢用戶語句

auth.jdbcAuthentication().usersByUsernameQuery("zhangsan");

4.1.2 使用默認的查詢權限語句

auth.jdbcAuthentication().authoritiesByUsernameQuery("zhangsan");

4.2 方式二:自定義UserDetailsService檢索用戶

4.2.1 實現UserDetailService接口loadUserByUsername(String username)方法

4.2.2 實驗步驟

1 創建表結構

security實驗\security.sql

2 配置 configure(AuthenticationManagerBuilder auth)

@Autowired

UserDetailsService userDetailsService;//用戶詳情查詢服務組件的接口

 

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

//根據用戶名查詢出用戶的詳細信息

auth.userDetailsService(userDetailsService);

}

3 編寫UserDetailService實現:

  1. 接口及已有實現類

  1. 實現UserDetailService接口,提供自定義實現類

org.springframework.security.core.userdetails.UserDetailsService

package com.atguigu.security.component;

 

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.JdbcTemplate;

import org.springframework.security.core.authority.*;

import org.springframework.security.core.userdetails.User;

import org.springframework.stereotype.Service;

 

//按照用戶名查詢用戶詳情的接口

@Service

public class AppUserDetailsServiceImpl implements UserDetailsService {

@Autowired

JdbcTemplate jdbcTemplate;

 

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

String queryUser = "SELECT * FROM `t_admin` WHERE loginacct=?";

   

//1、查詢指定用戶的信息

Map<String, Object> map = jdbcTemplate.queryForMap(queryUser, username);

   

//2、將查詢到的用戶封裝到框架使用的UserDetails裏面

return new User(map.get("loginacct").toString(), map.get("userpswd").toString(),

AuthorityUtils.createAuthorityList("ADMIN","USER"));//暫時寫死,過後數據庫中查

}

}

4 運行測試結果,密碼不一致,跳轉到登錄頁,並提示錯誤消息

 

4.2.3 debug測試登錄-斷點調試

1 斷點-方法棧

2 自定義UserDetailService實現類

3 Dao層認證提供者: DaoAuthenticationProvider

Dao層認證提供者DaoAuthenticationProvider,用於調用自定義的UserDetailService實現類方法

4 抽象層用戶認證提供者: AbstractUserDetailsAuthenticationProvider

抽象層用戶認證提供者,獲取dao層查找的認證用戶信息,被封裝成UserDetails對象,User類是UserDetails接口實現類

 1)org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks認證用戶賬號是否被鎖定,是否啓用,是否過期;用戶表中可以增加這些字段。

2)public interface Authentication extends Principal 封裝表單提交的認證信息

    認證用戶名和密碼;鹽值爲null

 

   採用org.springframework.security.authentication.encoding.BasePasswordEncoder默認加密器對錶單提交明文加密(其實並沒有進行任何加密,明文無變化)

  1. 總結

4.3 基於數據庫(MD5密碼)認證 (debug)

使用數據庫保存/查詢用戶數據,完成認證功能

4.3.1 配置 configure(AuthenticationManagerBuilder auth)

org.springframework.security.crypto.password.PasswordEncoder

//測試:分析源碼(驗證密碼不一致)

auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);

4.3.2 引入MD5加密工具類:MD5Util.java

4.3.3 PasswordEncoder接口實現類:AppPasswordEncoder

@Service

public class AppPasswordEncoder implements PasswordEncoder {

 

/**

   * 密碼加密的算法

   */

@Override

public String encode(CharSequence rawPassword) {

String digestPwd = MD5Util.digestPwd(rawPassword.toString());

return digestPwd;

}

 

/**

   * 比較登錄密碼和數據庫存儲密碼是否一致

   * rawPassword:頁面的明文密碼

   * encodedPassword:數據庫的密文密碼

   */

@Override

public boolean matches(CharSequence rawPassword, String encodedPassword) {

//使用自己的工具類

String digestPwd = MD5Util.digestPwd(rawPassword.toString());

return digestPwd.equals(encodedPassword);

} 

}

4.3.4 Debug測試,主要測試matches方法的調用過程

  1. 表單提交密碼:rawPassword

  1. 數據庫存儲密碼 :encodePassword

  1. 調用自定義密碼驗證器

  1. 密碼不一致,拋異常:Bad credentials ;密碼一致,通過認證

  1. 創建UsernamePasswordAuthenticationToken 對象,封裝認證信息

4.3.5 源碼參考

protected Authentication createSuccessAuthentication(Object principal,Authentication authentication, UserDetails user) {

// Ensure we return the original credentials the user supplied,

// so subsequent attempts are successful even with encoded passwords.

// Also ensure we return the original getDetails(), so that future

// authentication events after cache expiry contain the details 

UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(

principal, authentication.getCredentials(),

authoritiesMapper.mapAuthorities(user.getAuthorities())  );//封裝用戶權限信息 

result.setDetails(authentication.getDetails()); //封裝用戶信息

return result;

}

1 principal 認證主體-數據庫中查詢User數據

2 authentication.getCredentials() 認證密碼(表單中密碼)

3 authoritiesMapper.mapAuthorities(user.getAuthorities()) 認證權限集合

該用戶擁有的權限,暫時寫死在代碼中的,後期要根據用戶查詢所擁有的權限

4 認證細節:包括客戶端ip和sessionid

org.springframework.security.web.authentication.WebAuthenticationDetails

5 result對象(UsernamePasswordAuthenticationToken)詳細描述

4.4 基於數據庫(BCryptPasswordEncoder)密碼加密認證

4.4.1 PasswordEncoder接口

4.4.2 使用BCryptPasswordEncoder進行密碼加密

//推薦密碼加密器用這個BCryptPasswordEncoder; 將一個字符串加密成一個永不重複的密文

//1、加鹽+加隨機數

auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());

4.4.3 本地測試:main方法

public static void main(String[] args) {

BCryptPasswordEncoder pe = new BCryptPasswordEncoder();

 

//$2a$10$WzKk37ncOPynOSxyFGkxWu3ys7xaf7L/9uUhfVYVOCFTqeHkgJvOq

//$2a$10$VmWwIx/uxNQabCYl3I5mZ.U9sQvpiM/xAhX69Skg0EWyDm3twQfcO

//$2a$10$2Ig1mxqlb033XcU7aB0Ck.OZouRLsHUkJyIl9Mzi40FIY6grcEUr6

//大致的規律:$2a$10$+"xxx"+"/"+"xxx"

String encode = pe.encode("123456");

System.out.println(encode);

}

4.4.4 服務器運行測試

將main方法生成的密文存儲到數據庫中(注意:userpswd字段長度),重新啓動服務器進行測試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章