传智专修学院吧 关注:728贴子:11,281
  • 1回复贴,共1

Spring Security + JWT实现前后端分离下的认证授权(4)

只看楼主收藏回复

此文转载于知乎大佬RudeCrab,好文分享.
认证异常处理器AuthenticationEntryPoint
该接口也只有一个方法:
public interface AuthenticationEntryPoint { /** * 接收异常并处理 */ void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException);}
我们来自定义一个类实现我们自己的错误处理逻辑:
public class MyEntryPoint implements AuthenticationEntryPoint { @override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException { response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); // 直接提示前端认证错误 out.write("认证错误"); out.flush(); out.close(); }}
用户传递过来账号密码 认证校验 异常处理,这一整套流程的组件我们就都给定义完了!现在只差最后一步,就是在Spring Security配置类里面进行一些配置,才能让这些生效。
配置
Spring Security对哪些接口进行保护、什么组件生效、某些功能是否启用等等都需要在配置类中进行配置,注意看代码注释:
@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { @autowired private UserServiceImpl userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { // 关闭csrf和frameOptions,如果不关闭会影响前端请求接口(这里不展开细讲了,感兴趣的自行了解) http.csrf().disable(); http.headers().frameOptions().disable(); // 开启跨域以便前端调用接口 http.cors(); // 这是配置的关键,决定哪些接口开启防护,哪些接口绕过防护 http.authorizeRequests() // 注意这里,是允许前端跨域联调的一个必要配置 .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() // 指定某些接口不需要通过验证即可访问。登陆、注册接口肯定是不需要认证的 .antMatchers("/API/login", "/API/register").permitAll() // 这里意思是其它所有接口需要认证才能访问 .anyRequest().authenticated() // 指定认证错误处理器 .and().exceptionHandling().authenticationEntryPoint(new MyEntryPoint()); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 指定UserDetailService和加密器 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @bean @Override protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }}
其中用的最多的就是configure(HttpSecurity http)方法,可以通过HttpSecurity进行许多配置。当我们重写这个方法时,就已经关闭了默认的表单登录方式,然后我们再配置好启用哪些组件、指定哪些接口需要认证,就搞定了!
假设现在我们有一个/API/test接口,在没有登录的时候调用该接口看下效果:

我们登录一下:

然后再调用测试接口:

可以看到未登录时测试接口是无法正常访问的,会按照我们在EntryPoint中的逻辑返回错误提示。
总结和补充
有人可能会问,用AuthenticationManager认证方式要配置好多东西啊,我就用之前说的那种最简单的方式不行吗?当然是可以的啦,用哪种方式都随便,只要完成功能都行。其实不管哪种方式我们的认证的逻辑代码一样都没少,只不过一个是我们自己业务类全部搞定,一个是可以集成框架的组件。这里也顺带再总结一下流程:
用户调进行登录操作,传递账号密码过来 登录接口调用AuthenticationManager
根据用户名查询出用户数据 UserDetailService查询出UserDetails
将传递过来的密码和数据库中的密码进行对比校验 PasswordEncoder
校验通过则将认证信息存入到上下文中 将UserDetails存入到Authentication,将Authentication存入到SecurityContext
如果认证失败则抛出异常 由AuthenticationEntryPoint处理
刚才我们讲的认证方式都是基于session机制,认证后Spring Security会将Authentication存入到session中,Key为HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY。也就是说,你完全可以通过如下方式获取Authentication:
Authentication = (Authentication)session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY)
当然,官方还是不推荐这样直接操作的,因为统一通过SecurityContextHolder操作更利于管理!使用SecurityContextHolder除了获取当前用户外,退出登录的操作也是很方便的:
@GetMapping("/logout")public String logout() { SecurityContextHolder.clearContext(); return "退出成功";}
session认证咱们就讲解到此,接下来咱们讲解JWT的认证。


IP属地:江苏1楼2021-05-06 10:21回复
    第一


    IP属地:江苏来自Android客户端2楼2023-09-17 16:46
    回复