1. 整体逻辑
1. SpringSecurity认证的逻辑规则
启动项目时,SpringBoot自动检索所有带@Configuration
的注解,所以就将我们的WebSecurityConfig
给加载了,这个config中,我们需要在configure(AuthenticationManagerBuilder auth)
方法中注册一个继承自UserDetailsService
的接口,这个接口中只有一个方法,那就是使用username
获取到数据库中用户信息并返回成UserDetail
实体。这个方法需要我们按照我们的不同业务场景重写
WebSecurityConfig
/**
* @description:
* @author: coderymy
* @create: 2020-10-01 13:54
* <p>
* 1\. 创建WebSecurityConfig 类继承WebSecurityConfigurerAdapter
* 2\. 类上加上@EnableWebSecurity,注解中包括@Configuration注解
* <p>
* WebSecurityConfigurerAdapter声明了一些默认的安全特性
* (1)验证所有的请求
* (2)可以使用springSecurity默认的表单页面进行验证登录
* (3)允许用户使用http请求进行验证
*/
/**
* 如何自定义认证
* 1\. 实现并重写configure(HttpSecurity http)方法,鉴权,也就是判断该用户是否有访问该api的权限
* <p>
* <p>
* 页面显示403错误,表示该用户授权失败(401代表该用户认证失败)前端可以使用返回的状态码来标识如何给用户展示
* 用2XX表示本次操作成功,用4XX表示是客户端导致的失败,用5XX表示是服务器引起的错误
*/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode("123");
System.out.println(encode);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
/**
* SpringSecurity5.X要求必须指定密码加密方式,否则会在请求认证的时候报错
* 同样的,如果指定了加密方式,就必须您的密码在数据库中存储的是加密后的,才能比对成功
*
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//鉴权
@Override
protected void configure(HttpSecurity http) throws Exception {
/**
* 1\. HttpSecurity被声明为链式调用
* 其中配置方法包括
* 1\. authorizeRequests()url拦截配置
* 2\. formLogin()表单验证
* 3\. httpBasic()表单验证
* 4\. csrf()提供的跨站请求伪造防护功能
*/
/**
* 2\. authorizeRequests目的是指定url进行拦截的,也就是默认这个url是“/”也就是所有的
* anyanyRequest()、antMatchers()和regexMatchers()三种方法来拼配系统的url。并指定安全策略
*/
http.authorizeRequests()
//这里指定什么样的接口地址的请求,需要什么样的权限 ANT模式的URL匹配器
.antMatchers("/select/**").hasRole("USER")//用户可以有查询权限
.antMatchers("/insert/**").hasRole("ADMIN")//管理员可以有插入权限权限
.antMatchers("/empower/**").hasRole("SUPERADMIN")//超级管理员才有赋权的权限
.antMatchers("/login/**").permitAll()//标识list所有权限都可以直接访问,即使不登录也可以访问。一般将login页面放给这个权限
.and()
.formLogin()
// .loginProcessingUrl("/login/user")//用来定义什么样的API请求时login请求
// .permitAll()//login请求需要是所有权限都可以的
.and().csrf().disable();
/**
* 将自定义的JWT过滤器加入configure中
*/
JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter(this.authenticationManager());
http.addFilterBefore(jwtAuthenticationFilter, JWTAuthenticationFilter.class);
}
@Autowired
private MyUserDetailsService myUserDetailsService;
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
}
MyUserDetailsService
@Service
public class MyUserDetailsService implements UserDetailsService {
@Resource
private UsersRepository usersRepository;
/**
* 其实这样就完成了认证的过程,能获取到数据库中配置的用户信息
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//获取该用户的信息
Users user = usersRepository.findByUsername(username);
if (user == null) {//用户不存在报错
throw new UsernameNotFoundException("用户不存在");
}
/**
* 将roles信息转换成SpringSecurity内部的形式,即Authorities
* commaSeparatedStringToAuthorityList可以将使用,隔开的角色列表切割出来并赋值List
* 如果不行的话,也可以自己实现这个方法,只要拆分出来就可以了
*/
//注意,这里放入Authorities中的信息,都需要是以Role_开头的,所以我们在数据库中配置的都是这种格式的。当我们使用hasRole做比对的时候,必须要是带Role_开头的。否则可以使用hasAuthority方法做比对
user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
return user;
}
}
其实如果去掉上面的将自定义的JWT过滤器加入到过滤链中的话,这个认证过程已经完成了。使用下面的代码就可以调用起整个认证程序。
核心代码
authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userDto.getUsername(), userDto.getPassword()));
这一行就会将username和password放到认证程序中进行认证。
也就是需要我们自己也逻辑让他去触发这个代码的实现。就可以自动完成认证程序了。就会触发使用username获取到数据库用户信息,然后经过密码加密比对之后会将认证结果返回。
我们整合JWT其实也很简单,其实就是将JWT的登录部分的操作,使用过滤器封装,将该过滤器放到整个认证的过滤链中
2. 自定义JWT过滤器的配置
SpringSecurity过滤器的配置无非以下几个条件
- 该过滤器过滤什么样的API请求(也就是说什么样的API请求会触发该过滤器执行)。配置被过滤的请求API
- 该过滤器做什么事
- 该过滤器执行成功以及执行失败的各种情况该怎么做
- 该过滤器执行的时机是什么样的,也就是在过滤链之前还是之后执行
先解决逻辑上以上三个问题的答案
- 我们需要拦截认证请求,肯定是形如
xxx/login/xxx
这种API接口的请求啦 - 这个过滤器会做什么事呢?
- 首先,我们需要进行用户名密码的基础验证,也就是合不合法
- 我们需要