简介
Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
入门案例
直接进行Spring Security的基本使用
- 引入spring boot与spring security的依赖
- 添加一个配置类,配置拦截的路径
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //表单登录
.and()
.authorizeRequests() //认证配置
.anyRequest() //任何请求
.authenticated(); //需要身份验证
}
}
- 编写一个简单的controller 测试
- 启动项目,访问请求地址,可以看到不是预先想的那样输出一个字符串,而是有一个登陆页面
- 如果没有配置帐号密码的话,spring security的默认登录名是user,密码则在启动项目中有打印出来
- 输入后点击登陆,可以看到成功跳转至我们预想的页面
spring security的基本原理
- 其实如果对serverlet有一定了解的话,不难猜出,spring security本质上就是一套过滤器链
不难看出,
UsernamePasswordAuthenticationFilter应该是和用户密码有关的过滤器,ExceptionTranslationFilter则是对异常进行处理的过滤器
这里先学习一下spring security的基础使用,源码剖析暂且放下
两个接口
UserDetailsService
- 当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。 所以我们要通过自定义逻辑控制认证逻辑。如果需要自定义逻辑的话,则需要实现
UserDetailsService接口 - 该接口的返回值是
UserDetails接口 - User实现了该接口,以后我们只要使用User实体类即可
PasswordEncoder
-
该接口是进行密码校验处理的一个接口
-
String encode(CharSequence var1);表示把参数按照特定的解析规则进行解析 -
boolean matches(CharSequence var1, String var2);表示验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码。第二个参数表示存储的密码。 -
default boolean upgradeEncoding(String encodedPassword) { return false; }表示如果解析的密码能够再次进行解析且达到更安全的结果则返回 true,否则返回 false。默认返回 false。 -
BCryptPasswordEncoder是 Spring Security 官方推荐的密码解析器,平时多使用这个解析器。 -
BCryptPasswordEncoder是对 bcrypt 强散列方法的具体实现。是基于 Hash 算法实现的单向加密。可以通过 strength 控制加密强度,默认 10
数据库认证实现登录
首先准备sql以及实体类,还有相应的ORM框架,这里就不再赘述,我这里为了方便使用了mp以及lombok
- 前面说到,如果自定义用户名密码进行验证,则要实现
UserDetailsService接口,并且在里面进行数据库的操作,进行编写 - 创建配置类
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
super.configure(auth);
}
@Bean
public PasswordEncoder password(){
return new BCryptPasswordEncoder();
}
}
- 编写实现类
@Service("userDetailsService")
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UsersMapper usersMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
QueryWrapper<Users> wrapper = new QueryWrapper<>();
// s 就是表单中传过来的username的值
wrapper.eq("username",s);
Users users = usersMapper.selectOne(wrapper);
if (users==null){
throw new UsernameNotFoundException("用户名不存在");
}
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
- 这里只是作一个简单的逻辑判断,只要用户名密码存在于数据库,都能成功进行跳转