原理
Spring Security 本质上是一个过滤器链,含有很多过滤器,从启动是可以获取到过滤链的。(JAVA WEB 过滤器)
使用Spring Boot 提供了自动化配置 Spring Security 的方案,可以使用更少的配置来使用 Spring Security。
其中有三个较为重要的过滤器:
-
FilterSecurityInterceptor:是一个方法级的权限过滤器,基本位于过滤链的最底部。
-
ExceptionTranslationFilter:是一个异常过滤器,用来处理在认证授权过程中抛出的异常。
-
UsernamePasswordAuthenticationFilter:对/login 的 POST 请求做拦截,检验表中的用户名、密码。
UserDetailsService接口----自定义逻辑
当我们对Spring Security没有任何配置的时候,用户账号密码都是由Spring Security定义默认生成的。用户名默认为 username,密码自动在每次运行时在控制台生成。
自定义逻辑控制认证逻辑,从数据库中查询用户的ID和密码写在该接口下。该接口的主要作用是:查询数据库用户ID和密码。
UserDetailsService接口下有一个抽象方法,在不同的项目下,根据实际情况创建实现类重写这个方法,实现查询数据库查询用户名和ID的功能。
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
这个方法的返回类型也是接口,也需要有一个实现类,其各种抽象方法的作用如下:
Collection<? extends GrantedAuthority> getAuthorities():获取所有权限
String getPassword():获取密码
String getUsername():获取用户名
boolean isAccountNonExpired():验证用户是否过期
boolean isAccountNonLocked():验证用户账号是否被锁定
boolean isCredentialsNonExpired():验证凭证(密码)是否过期
boolean isEnabled():验证用户是否可用
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
在Spring Security中已经给我们提供了UserDetails的实体类User,其全名路径为org.springframework.security.core.userdetails.User
。在我们项目创建过程中,需要注意别将我们的User类和内置的User类混淆!
对于内置User类,里面提供了很多方法和属性,其中构造方法有两个,调用其中任何一个都可以实例化,而三个参数的构造方法实际上也是调用 7 个参数的构造方法。
username:
用户名
password:
密码
authorities
:用户具有的权限。此处不允许为 null
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
Assert.isTrue(username != null && !"".equals(username) && password != null, "Cannot pass null or empty values to constructor");
this.username = username;
this.password = password;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
}
在User类中的username应为客户端(client)传来的用户名,密码应为数据库查询到的密码。Spring Security会根据查询到的密码(password)和客户端传入的密码(password)进行比较。如果相同,则认证成功,否则认证失败。
authorities中的权限反映该用户的权限,即该用户所能做的事情,如果某个用户没有拥有某个权限而去访问某个需要权限的事情,就会出现403页面。
PasswordEncoder 密码解析器
Spring Security中要求IOC容器中必须要有PasswordEncoder的实例,故自定义登录逻辑时,需要给容器注入PasswordEncoder的Bean对象。
PasswordEncoder接口中含有三个方法:
encode()
:把参数按照特定的解析规则进行解析。
matches()
:验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码(即客户端传入的密码),第二个参数表示存储的密码(数据库中对应的密码)。
upgradeEncoding()
:如果解析的密码能够再次进行解析且达到更安全的结果则返回 true,否则返回 false。默认返回 false。
public interface PasswordEncoder {
String encode(CharSequence var1);
boolean matches(CharSequence var1, String var2);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
在测试类中测试PasswordEncoder的方法:
/**
* 测试方法 BCryptPasswordEncoder 用法
*/
public class BCryptPasswordEncoderTest {
@Test
public void test(){
//创建解析器
PasswordEncoder pw = new BCryptPasswordEncoder();
//对密码加密
String encode = pw.encode("123");
System.out.println("根据Hash加密后的密码:");
System.out.println(encode);
//判断原字符和加密后内容是否匹配
boolean matches = pw.matches("123", encode);
// boolean matches = pw.matches("124", encode);
System.out.println("=============================\n"
+"判断原字符和加密后的内容是否相同");
System.out.println(matches);
}
}
客户端的password和数据库中的password相同时,结果为:
客户端的password和数据库中的password不同时,结果为:
此处的测试没有引入数据库,直接赋值进行比较观察结果。引入数据库的操作需先在依赖中引入相关依赖,再配置数据库,通过mybatis或者mybatisplus或者JPA去执行查询语句进行查询,再比较。