spring security 5 (10)-digest摘要认证

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangb_java/article/details/86714105

摘要认证和基本认证一样,也是由http协议定义的,对用户使用的角度来说,它们并没有区别。但是摘要认证的安全性要高出很多,并且永远不会以明文传输密码。必须先理解基本认证

基本配置

  • spring security内置了DigestAuthenticationFilter,封装了摘要认证逻辑,不需要你去构建AuthenticationManager。但是构建UserDetailsService是必不可少的。
  • 同时内置了DigestAuthenticationEntryPoint,封装了摘要认证的专用登录入口。
	public void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
				.anyRequest().authenticated().and()
				.addFilter(digestAuthenticationFilter())//在过滤链中添加摘要认证过滤器
				.exceptionHandling()
					.authenticationEntryPoint(digestAuthenticationEntryPoint())//摘要认证入口端点
				.and()
				.csrf().disable(); 
	}
	@Bean
	public DigestAuthenticationFilter digestAuthenticationFilter(){
		DigestAuthenticationFilter filter= new DigestAuthenticationFilter();
		filter.setAuthenticationEntryPoint(digestAuthenticationEntryPoint());//必须配置
		filter.setUserDetailsService(userDetailsService());//必须配置
		return filter;
	}
	@Bean
	public DigestAuthenticationEntryPoint digestAuthenticationEntryPoint() {
		DigestAuthenticationEntryPoint point = new DigestAuthenticationEntryPoint();
		point.setRealmName("realm");//realm名
		point.setKey("key");//密钥
		return point;
	}
	@Bean
	public UserDetailsService userDetailsService() {
		return new UserDetailsService() {
			public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
				//省略从数据库查询过程
				String password = "123";
				List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
				authorities.add(new SimpleGrantedAuthority("auth"));
				return new User(username, password, true, true, true, true, authorities);
			}
		};
	}

认证基本流程

和基本认证一样,请求url,会弹框要求输入用户名/密码。但响应头Basic变成了Digest,还多了两个参数:qop和nonce。

  • Digest:指摘要认证。
  • qop:该值决定了生成摘要的具体计算公式,及参与计算的参数。本篇仅以spring security默认公式为例。
  • nonce:相当于随机数。计算公式为base64(时间戳:md5(时间戳:key))。首先由时间戳和后台配置的key值,生成md5散列值,再用时间戳和md5值进行base64编码。之后客户端登录请求头都必须有nonce参数,服务端收到nonce,解码出时间戳,将其和服务端的key重新生成nonce,比对一致,由此证明请求头的nonce就是服务端原始发放的nonce,请求合法。  
  • 时间戳:服务端从nonce解码的时间戳,还会和当前时间进行比较。如果超时则验证失败,并刷新nonce放在响应头,同时增加一个响应参数stale=true。浏览器根据该参数,自动使用新的nonce重新发起认证请求,整个过程用户无感知。spring security中默认超时为5分钟,如果黑客拦截到用户认证参数,最多只能有效利用5分钟,即使他刷新nonce,不知道用户密码是无法重新认证的,下面会讲。
  • 理论上md5散列值是不可逆的,所以黑客无法擅改nonce中的时间戳,否则会造成服务端验证nonce失败。又因为黑客不知道服务端key,所以其无法伪造nonce。

输入用户名,密码123,将和loadUserByUsername方法的返回值比对一致,认证通过。以后每次请求都会在请求头加入以下内容

  • Digest:摘要认证类型。
  • username:登录时输入的用户名。
  • realm:这是之前服务器响应的参数,原样返回,上一篇讲过。。。
  • nonce:同上,并且之后每次请求,都会用上面的方法对其进行认证。
  • uri:可在后台比对实际请求路径是否一致,否则有被黑客攻击的可能性。
  • response:将所有Authorization参数,以及用户输入的密码等,共同生成的md5摘要。服务端收到请求,会用同样的方法,并查询数据库中的用户密码再次生成摘要进行对比,只要有一个参数被擅改,结果都不一致。所以不知道用户密码,是无法生成一致摘要的。
  • qop:源自服务器
  • nc:16进制数,使用当前nonce请求次数,服务端可对其进行限制,达到一定次数主动刷新nonce,以防止被黑客利用。
  • cnonce:客户端生成的随机数,也会参与摘要计算,使得每次请求的response值都不一样。服务端可以保存每次请求的cnonce,如果发现其值已经存在,很可能是黑客攻击。

spring security只对核心参数如nonce做了验证,一些非核心参数验证,可以自行了解扩展。一般浏览器基本都支持摘要认证,用户输入用户名/密码后浏览器会自动生成并添加请求头所有参数,并自动缓存。

双向认证

完整的摘要认证还有一些扩展,我在spring security中并没有找到,如双向认证。服务端收到客户端的随机数后,基于它再加入用户密码等参数来生成响应md5,放在响应头中。客户端使用自己原始的随机数和密码等再生成一次md5,比对一致,从而验证服务端的真实性。

总结

摘要认证和基本认证都是早期的认证方法,现在已经用的越来越少,其验证过程比较繁琐,而且md5现在都有公开的破解网站了。但是其整体安全思路放在今天仍然是不错的防御策略,非常值的学习借鉴。

猜你喜欢

转载自blog.csdn.net/wangb_java/article/details/86714105