当前位置:   article > 正文

SpringSecurity身份认证方式及底层源码学习_basicauthenticationconverter#convert

basicauthenticationconverter#convert

SpringSecurity分为两种

  • HttpBasic 是弹窗认证方式。
  • HttpForm 是表单认证方式。

它们都有默认的页面来使用。

1. 首先我们需要创建配置类,此类需要加上@EnableWebSecurity证明 启动springSecurity过滤链功能,需要重写两个方法

/**
   身份认证过滤器
2. 认证信息提供方式(用户名,密码,当前用户的资源权限)
3. 可采的内存存储方式,也可采用数据库方式等。
*/
configure(AuthenticationManagerBuilder auth);

/**
 资源权限配置(过滤器链)
4. 拦截哪些资源
5. 资源所对应的角色权限
 3. 定义认证方式 httpbasic  httpform
6. 定制登录页面,登录请求地址,错误处理方式
7. 自定义 springsecurity 过滤器等
*/
configure(HttpSecurity http)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2. 我们先了解HttpBasic认证方式

protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic()
                .and()
                .authorizeRequests()//认证请求
                .anyRequest().authenticated() //所有进入应用的http请求都要进行认证
        ;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用HttpBasic 认证方式, 会使用security默认的账号和密码,账号为user 密码 是会自动生成一个
自动生成密码

登录界面
3. 基于内存存储认证信息
上面的用户名和密码是security为我们提供的,下面基于内存存储自定义的用户名密码。

//使用这两个方法来指定账号 密码
withUser("andy")//账号
password("1234")//密码
  • 1
  • 2
  • 3

3.1 重启后报错

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
  • 1

这时查看下 PasswordEncoder 的源码

public interface PasswordEncoder {
    // 用于明文加密
    // 我们来调用,一般在注册的时候,将前端传过来的密码,进行加密后保存进数据库
    String encode(CharSequence var1);

    // 输入密码与数据库密码进行对比
    // 明文与密文的对比
    boolean matches(CharSequence var1, String var2);

    //是否需要再次进行编码增加安全性,默认不需要
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注:

  1. 在spring security 5.0 版本之前,加密的PasswordEncoder 接口默认实现NoOpPasswordEncoder 这个是可以不用加密的,直接使用明文密码存储,当前已经标注过时了。
  2. 在spring security 5.0 版本之后,默认实现类改为 DelegatingPasswordEncoder,这个实现类,要求我们必须对加密后进行存储。

3.2 解决错误

指定BCryptPasswordEncoder加密方式相同的密码, 在每次加密后的结果都不一样的,因为它每次都会随机生成盐值, 会将随机生成的盐加到密码串中
每次判断时, 通过随机生成的盐反推回加密时的密码串, 最终判断是否匹配
加密的最终结果分为两部分,盐值 + MD5(password+盐值), 调用 matches(…) 方法的时候,先从密文中得到
盐值,用该盐值加密明文和最终密文作对比,
这样可以避免有一个密码被破解, 其他相同的密码的帐户都可以破解.因为通过当前机制相同密码生成的密文都
不一样。
加密过程(注册): aaa (盐值) + 123(密码明文) > 生成密文 > 最终结果 盐值.密文:aaa.asdlkf 存入数据库
校验过程(登录): aaa (盐值, 数据库中得到) + 123(用户输入密码)> 生成密文 aaa.asdlkf,与数据库对比一
致密码正确

4. httpform 表单认证方式
HTTP form表单默认页面

底层代码了解
spring security采用过滤器链实现认证与授权

  1. UsernamePasswordAuthenticationFilter : 请求中包含用户名密码则进行认证,认证成功标记认证成功,否则进入下一个认证过滤器(HTTP form请求方式)
  2. BasicAuthenticationFilter :请求头有basic开头的信息,base64解码后认证,认证成功则标记认证成功,否则进入下一认证过滤器(前提是使用了httpbasic认证方式,如果使用HTTP form认证方式则不会进入此过滤器)
  3. 其他认证过滤器
  4. ExceptionTranslationFilter :捕获异常处理后续处理
  5. FilterSecurityInterceptor : 认证通过后,根据资源权限配置来判断当前请求是否可以访问对应资源。(1 2 3 属于用户认证,只属于第一道门槛,这里会根据 config 中配置的对应权限再次进行鉴权,如果没有对应的可访问资源 那就会抛出异常 4 )
  6. Controller 对应资源
//BasicAuthenticationFilter 中的 BasicAuthenticationConverter.convert 方法
-- 152UsernamePasswordAuthenticationToken authRequest = authenticationConverter.convert(request);
//获取请求头  Authorization
-- 80 String header = request.getHeader(AUTHORIZATION);
//判断是否存在 basic
-- 86 if (!StringUtils.startsWithIgnoreCase(header, AUTHENTICATION_SCHEME_BASIC)) {
			return null;
		}
// 大致是 这样  basic username:password 封装,在进行basic64 编码,拿到封装的数据在进行解码,再取对应的username 和password
-- 107  int delim = token.indexOf(":");
		if (delim == -1) {
			throw new BadCredentialsException("Invalid basic authentication token");
		}
		UsernamePasswordAuthenticationToken result  = new UsernamePasswordAuthenticationToken(token.substring(0, delim), token.substring(delim + 1));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//如果抛错 进入ExceptionTranslationFilter 
-- 118 chain.doFilter(request, response);  //会直接放行,抛错在cache中会捕获,会进行重定向
  • 1
  • 2
//FilterSecurityInterceptor
//核心方法invoke  这里会根据config 中配置的对应权限再次进行鉴权,如果没有对应的可访问资源 那就会抛出异常
-- 123 InterceptorStatusToken token = super.beforeInvocation(fi);
  • 1
  • 2
  • 3

打上对应断点,我们来跟一下,
重启后 localhost/index 进入 FilterSecurityInterceptor invoke方法
why?

//对于UsernamePasswordAuthenticationFilter 来说它只接收 /login方法
-- 62 public UsernamePasswordAuthenticationFilter() {
		super(new AntPathRequestMatcher("/login", "POST"));
	}
//所以直接放行,进入FilterSecurityInterceptor 
//而目前config中设置的也是对所有的请求进行认证,所以当前的/index 访问会被拦截,
//抛错到ExceptionTranslationFilter cache中会重定向到登录页面。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这时候显示默认的登录页面
输入用户名以及密码,进入UsernamePasswordAuthenticationFilter

//他会构造一个token

-- 89 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
				username, password);
//进入 UsernamePasswordAuthenticationToken  中 核心方法
//首次进入会置为false 无权限
-- 58 setAuthenticated(false); 
//认证 username password 匹配
--95 return this.getAuthenticationManager().authenticate(authRequest);
//再次进入FilterSecurityInterceptor 放行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

下面是我的security学习demo ,感兴趣的可以看下
GitHub :https://github.com/Andy-leoo/mxg-security.git
下面是 spring security中文文档,大家可以看看
https://www.springcloud.cc/spring-security-zhcn.html#what-is-acegi-security

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/124336
推荐阅读
相关标签
  

闽ICP备14008679号