- 认证
解决我是谁
- 授权
解决我能做什么
1.1 原理 1.2 常见的过滤器BasicAuthenticationFilter
- 如果在请求中找到一个Basic Auth Http 头,如果找到,则尝试用该头中的用户名和密码验证用户。
UsernamePasswordAuthenticationFilter
- 如果在请求参数或者post的Request Body 中找到用户名和密码,则尝试用这些值对用户进行身份验证。
DefaultLoginPageGeneratingFlter
- 默认登录页面生成过滤器,用于生成一个登录页面,如果你没有明确地禁用这个功能,那么就会生成一个登录页面,这就是为什么在启用Spring Security 时,会得到一个默认登录页面的原因
DefaultLoginOutPageGeneratingFilter
- 没有禁用该功能,则会生成一个注销页面
FilterSecurityInterceptor
- 过滤安全拦截器。用于授权逻辑
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 设置配置 :用于配置路径的安全性
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests(req-> req.antMatchers("/test/**").authenticated())
.formLogin(AbstractHttpConfigurer::disable)
.httpBasic(withDefaults())
.csrf(AbstractHttpConfigurer::disable);
}
// 设置不拦截资源 常用于忽略某些资源
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers("/public/**");
}
}
1.3 Remember-me功能
为解决session过期用户的直接访问问题。
原理:使用cookie 存储用户名,过期时间,以及一个Hash
Hash:md5(用户名+过期时间+密码+key)
1.4 定制登录/退出登录的处理- 登录成功后的处理:AuthenticationSuccessHandle
- 登录失败后的处理:AuthenticationFailureHandle
- 退出登录成功后的处理 :LogoutSuccessHandler
将json转化为java对象
ObjectMapper objectMapper = new ObjectMapper();
String carJson =
"{ "brand" : "Mercedes", "doors" : 5 }";
Car car = objectMapper.readValue(carJson, Car.class);
将java对象转化为json
ObjectMapper objectMapper = new ObjectMapper(); Car car = new Car(); String json = objectMapper.writevalueAsString(car);1.5 自定义拦截器
继承需要定义的拦截器对其进行自定义配置
2 密码明文-哈希-加盐-自适应-?
自适应哈希:
配置迭代次数 md5(md5(“XX”))
配置随机的盐值
迭代次数和盐值存储在数据库中
Bcrypt,Scrypt,pdkdf2等
2.1 常用的JSR 380验证注解@NotNull : 验证注解属性值不为空。 @AssertTrue : 验证注解属性值是否为真 @Size : 验证注解的大小介于属性min和max之间 @Min : 验证注解属性的值不小于值属性的值 @Max : 验证被注解的值不大于值属性的值 @Email : 验证注解的属性是一个有效的电子邮件地址 @Pattern : 验证注解的属性是否匹配正则表达式 @NotEmpty : 验证属性不是空或空;应用于string @NotBlank : 只能用于文本值,验证属性不是空的 @Positive : 使用于数值,验证它们是严格意义上的正数 @Negative : 适用于数值,验证他们是严格意义上的负值 @Past : 验证一个日期值是在过去 @Future : 验证一个日期值是在未来2.2 自定义验证
- 自定义邮件注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EmailValidator.class)
public @interface ValidEmail {
String message () default "Invalid Email";
Class> [] groups() default {};
Class extends Payload>[] payload() default {};
}
- 自定义邮件注解配置,利用正则表达式进行解析定义验证逻辑
public class EmailValidator implements ConstraintValidator2.3 密码的验证规则{ // 自定义正则表达式 private final static String EMPTY_PATTERN=""; @Override public void initialize(ValidEmail constraintAnnotation) { ConstraintValidator.super.initialize(constraintAnnotation); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return validateEmail(s); } private boolean validateEmail(final String email){ // 对正则表达式进行解析 Pattern pattern = Pattern.compile(EMPTY_PATTERN); Matcher matcher = pattern.matcher(email); return matcher.matches(); } }
密码的验证比较复杂,使用Passay框架进行验证,封装验证逻辑在注解中有效的剥离验证逻辑和业务逻辑
- 引入passay
org.passay passay ${passay.version}
- 自定义密码验证
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PasswordConstraintValidator.class)
public @interface ValidPassword {
String message () default "Invalid Password";
Class> [] groups() default {};
Class extends Payload>[] payload() default {};
}
- 使用passay自定义验证逻辑
public class PasswordConstraintValidator implements ConstraintValidator2.4 比较dto中两个字段是否相等(如 密码,以及再次输入密码){ @Override public void initialize(ValidPassword constraintAnnotation) { ConstraintValidator.super.initialize(constraintAnnotation); } @Override public boolean isValid(String password, ConstraintValidatorContext context) { PasswordValidator validator = new PasswordValidator( Arrays.asList( //定义长度 6-20 new LengthRule(6,20), //定义最少拥有一个英文字符 new CharacterRule(EnglishCharacterData.Alphabetical,1), //定义至少拥有一个特殊字符 new CharacterRule(EnglishCharacterData.Special,1), //禁止5个连续的字符 new IllegalSequenceRule(EnglishSequenceData.Alphabetical,5,false), //禁止五个连续的数字 new IllegalSequenceRule(EnglishSequenceData.Numerical,5,false), // 不允许空格 new WhitespaceRule())); RuleResult result = validator.validate(new PasswordData(password)); if (result.isValid()) { return true; } //设置校验失败后的消息 context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(String.join(",", validator.getMessages(result))) .addConstraintViolation(); return false; } }
- 定义密码验证逻辑
public class PasswordMatchesValidator implements ConstraintValidator2.5 异常的统一处理{ @Override public void initialize(final PassWordMatches constraintAnnotation) { } @Override public boolean isValid(final UserDto obj, final ConstraintValidatorContext context) { UserDto user = (UserDto) obj; return user.getPassword().equals(user.getMatchPassword()); } }
使用网上开源zalando
3 Spring Security 框架解析org.zalando problem-spring-web
基于数据库认证,实现一个可定制化的基础模型。
spring security 相关的单元测试。
3.1 核心组件SecurityContext
- 用来存储当前认证的用户的详细信息
SecurityContextHolader
- 是一个工具类,提供了对安全上下文的访问,默认情况下使用ThreadLocal 对象来存储安全上下文
Authentication
- 存储了当前用户的详细信息
- Principal 可以理解为用户信息
- credentials 可以理解为密码
- Authorities 可以理解为权限
UserDetails在这里代表用户,而UserDetailsService 代表从用户数据库中调取用户形成UserDetails,这两个对象不负责认证工作只是提供数据,一般通过扩展他们来实现自定义的数据库结构
3.2 userDetails 和userDetailsService- userDetails代表用户,具备一系列属性约束
- 从数据存储中根据用户名找到用户,是由UserDetailsService定义的
本身不负责认证,只是提供数据,一般通过扩展他们来实现自定义的数据库结构
扩展方式
-
系统内建了jdbcuserDetailsManager,可以通过构造不同的sql进行深度的定制化。
-
也可以实现自己的userDetails和userDetailsService 实现定制



