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字段长度),重新启动服务器进行测试。

发布了227 篇原创文章 · 获赞 77 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/m2606707610/article/details/104099358