SpringSecurity分为两种
- HttpBasic 是弹窗认证方式。
- HttpForm 是表单认证方式。
它们都有默认的页面来使用。
1. 首先我们需要创建配置类,此类需要加上@EnableWebSecurity证明 启动springSecurity过滤链功能,需要重写两个方法
/**
身份认证过滤器
2. 认证信息提供方式(用户名,密码,当前用户的资源权限)
3. 可采的内存存储方式,也可采用数据库方式等。
*/
configure(AuthenticationManagerBuilder auth);
/**
资源权限配置(过滤器链)
4. 拦截哪些资源
5. 资源所对应的角色权限
3. 定义认证方式 httpbasic httpform
6. 定制登录页面,登录请求地址,错误处理方式
7. 自定义 springsecurity 过滤器等
*/
configure(HttpSecurity http)
2. 我们先了解HttpBasic认证方式
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()//认证请求
.anyRequest().authenticated() //所有进入应用的http请求都要进行认证
;
}
使用HttpBasic 认证方式, 会使用security默认的账号和密码,账号为user 密码 是会自动生成一个
3. 基于内存存储认证信息
上面的用户名和密码是security为我们提供的,下面基于内存存储自定义的用户名密码。
//使用这两个方法来指定账号 密码
withUser("andy")//账号
password("1234")//密码
3.1 重启后报错
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
这时查看下 PasswordEncoder 的源码
public interface PasswordEncoder {
// 用于明文加密
// 我们来调用,一般在注册的时候,将前端传过来的密码,进行加密后保存进数据库
String encode(CharSequence var1);
// 输入密码与数据库密码进行对比
// 明文与密文的对比
boolean matches(CharSequence var1, String var2);
//是否需要再次进行编码增加安全性,默认不需要
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
注:
- 在spring security 5.0 版本之前,加密的PasswordEncoder 接口默认实现NoOpPasswordEncoder 这个是可以不用加密的,直接使用明文密码存储,当前已经标注过时了。
- 在spring security 5.0 版本之后,默认实现类改为 DelegatingPasswordEncoder,这个实现类,要求我们必须对加密后进行存储。
3.2 解决错误
指定BCryptPasswordEncoder加密方式相同的密码, 在每次加密后的结果都不一样的,因为它每次都会随机生成盐值, 会将随机生成的盐加到密码串中
每次判断时, 通过随机生成的盐反推回加密时的密码串, 最终判断是否匹配
加密的最终结果分为两部分,盐值 + MD5(password+盐值), 调用 matches(…) 方法的时候,先从密文中得到
盐值,用该盐值加密明文和最终密文作对比,
这样可以避免有一个密码被破解, 其他相同的密码的帐户都可以破解.因为通过当前机制相同密码生成的密文都
不一样。
加密过程(注册): aaa (盐值) + 123(密码明文) > 生成密文 > 最终结果 盐值.密文:aaa.asdlkf 存入数据库
校验过程(登录): aaa (盐值, 数据库中得到) + 123(用户输入密码)> 生成密文 aaa.asdlkf,与数据库对比一
致密码正确
4. httpform 表单认证方式
底层代码了解
spring security采用过滤器链实现认证与授权
- UsernamePasswordAuthenticationFilter : 请求中包含用户名密码则进行认证,认证成功标记认证成功,否则进入下一个认证过滤器(HTTP form请求方式)
- BasicAuthenticationFilter :请求头有basic开头的信息,base64解码后认证,认证成功则标记认证成功,否则进入下一认证过滤器(前提是使用了httpbasic认证方式,如果使用HTTP form认证方式则不会进入此过滤器)
- 其他认证过滤器
- ExceptionTranslationFilter :捕获异常处理后续处理
- FilterSecurityInterceptor : 认证通过后,根据资源权限配置来判断当前请求是否可以访问对应资源。(1 2 3 属于用户认证,只属于第一道门槛,这里会根据 config 中配置的对应权限再次进行鉴权,如果没有对应的可访问资源 那就会抛出异常 4 )
- 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));
//如果抛错 进入ExceptionTranslationFilter
-- 118 chain.doFilter(request, response); //会直接放行,抛错在cache中会捕获,会进行重定向
//FilterSecurityInterceptor
//核心方法invoke 这里会根据config 中配置的对应权限再次进行鉴权,如果没有对应的可访问资源 那就会抛出异常
-- 123 InterceptorStatusToken token = super.beforeInvocation(fi);
打上对应断点,我们来跟一下,
重启后 localhost/index 进入 FilterSecurityInterceptor invoke方法
why?
//对于UsernamePasswordAuthenticationFilter 来说它只接收 /login方法
-- 62 public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
//所以直接放行,进入FilterSecurityInterceptor
//而目前config中设置的也是对所有的请求进行认证,所以当前的/index 访问会被拦截,
//抛错到ExceptionTranslationFilter cache中会重定向到登录页面。
这时候显示默认的登录页面
输入用户名以及密码,进入UsernamePasswordAuthenticationFilter
//他会构造一个token
-- 89 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
//进入 UsernamePasswordAuthenticationToken 中 核心方法
//首次进入会置为false 无权限
-- 58 setAuthenticated(false);
//认证 username password 匹配
--95 return this.getAuthenticationManager().authenticate(authRequest);
//再次进入FilterSecurityInterceptor 放行
下面是我的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