使用spring Security + JWT 实现前后端分离登录(纯代码无原理) - Go语言中文社区

使用spring Security + JWT 实现前后端分离登录(纯代码无原理)


构建项目

代码已上传gitee

如果你看到了该文章说明对spring框架有一定了解,相信你已经知道如何通过springboot进行项目构建
springboot想要支持安全验证以及web支持只需要在pom(如果是maven)文件中引入以下starter

maven

//安全包
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
//web包
<dependency>
 <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

gradle

implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'

启动项目

  1. 浏览器输入 http://localhost:8080 默认情况下控制台会输出一串密码串
    在这里插入图片描述
  2. security 默认情况下会拦截所有请求 并生成默认登录页
  3. 输入用户 user 密码就是控制台中的密码串
    在这里插入图片描述
  4. 输入成功后出现以下页面(404是因为我们并没有配置/路由)(如果出现以下页面表示登录成功)
    在这里插入图片描述
  5. 默认情况下security使用是表单登录通过session 与cookie 进行认证的
  6. 我们打开浏览器控制台发现有一段cookie
    在这里插入图片描述
  7. 我们删除该cookie刷新浏览器发现又让重新登录 (说明我们猜想是正确的)
    在这里插入图片描述

如何自定义默认的登录名与密码呢?

只需要在application.yml中配置即可
在这里插入图片描述

自定义安全认证与授权

  1. 认证: 认证即系统通过用户凭据 (密码),token,密钥等进行身份认证的过程 证明你是谁
  2. 授权: 系统,通过认证以后给与你一定权限,例如一个按钮的点击等,赋予你能干什么的权限
  • 在springboot 中自定义security只需要继承 WebSecurityConfigurerAdapter 并且添加 @EnableWebSecurity 注解即可配置security
    • 我们创建一个类JwtSecurityConfig 继承 WebSecurityConfigurerAdapter
      在这里插入图片描述
    • 实现config 方法
      在这里插入图片描述

可能有些人要问为什么不是下图:
在这里插入图片描述
这是应为formLogin是表单登录基于session-cookie机制进行用户认证的,而前后端分离一般使用jwt 即用户状态保存在客户端中,
前后端交互通过api接口 无session生成,所以我们不需要配置formLogin

  • 我们需要禁用session
    在这里插入图片描述

  • 我们再次访问 http://localhost:8080
    在这里插入图片描述

  • 发现没有登录页面,而是返回403页面,这是因为security 检测到项目中有WebSecurityConfigurerAdapter 的实现类并配置了config,所以它认为用户要自定义security所以默认的表单认证失效(除非你配置了formLogin) 至于403 是因为我们配置了所有路径都需要通过认证(包括/路径)

  • 返回403 正是我们想要的,说明了security起作用了,拦截保护了所有请求,同时还赋予我们自定义功能

创建一个controller (添加一个方法login 作为登录入口)

在这里插入图片描述

  • 配置/login 为匿名访问 防止被拦截无法进行登录
    在这里插入图片描述
  • security登录关键类是 AuthenticationManager 认证管理器,如果我们什么都不配置默认使用的是ProviderManager 是ioc运行时注册的bean 我们无法在编译时声明注入到controller,所以需要我们把默认的ProviderManager 在编译期声明为bean
  • 配置 JwtSecurityConfig
    在这里插入图片描述

开始认证

在这里插入图片描述

  • 正常认证流程为下图(显然我们缺少查询用户信息这一步 ,security这么强大当然给我们留了接口)
    在这里插入图片描述
  • 如果你的用户信息来源于内存中那配置以下信息(JwtSecurityConfig)即可
    在这里插入图片描述
  • 对于我们实际项目来说用户信息可能存储方式多种多样 security也给我提供了一个接口用于扩展用户信息获取方式
    • 第一步创建一个类 MyUserDetailsServiceImpl 实现 UserDetailsService
      在这里插入图片描述
    • 第二步将该Service 配置给认证管理器(也就是把内存存储配置替换掉)
      在这里插入图片描述

认证管理器在进行认证时会调用loadUserByUsername 方法把客户端传递回来的用户名传给username参数 我们根据该参数去我们存储用户信息的地方调用用户信息并填充UserDetails对象返回即可,真正做密码匹配的是DaoAuthenticationProvider 它是认证管理器AuthenticationManager 的子类 ,默认情况我们无需配置该类。

  • 这里为了测试我简单硬编码用户信息
    在这里插入图片描述
  • 访问 /login 进行登录 (选中你们喜欢的客户端(postman)进行测试)我使用apipost
    在这里插入图片描述
  • 如果我们把用户名写错看看会发生什么事
    在这里插入图片描述
    在这里插入图片描述

我们发现并没有我们期待的事发生控制台并没有报错 抛出 UsernameNotFoundException 异常,并且客户端返回403异常,为什么会出现这种问题? 因为security在默认情况(用户未配置认证异常处理时候会默认返回403异常),那么我们怎么自定义呢?

  • 创建一个类 MyAuthenticationEntryPoint 实现 AuthenticationEntryPoint 该接口只有一个方法
    在这里插入图片描述

参数 authException 即 认证错误所抛出的异常 我们根据异常重写reponse 给客户端返回认证错误即可。

  • 配置异常处理类
    在这里插入图片描述
  • 我们再次登录
    在这里插入图片描述

我们发现控制台报错 Bad credentials 出现这种错误一般是用户名密码错误,权限不够,密码不匹配等异常。通过debug发现在AbstractUserDetailsAuthenticationProvider 中有以下代码
在这里插入图片描述
其实security这样做主要为了安全考虑 (我们也推荐最好不要关闭该开关)但是有小伙伴说我就要抛出UsernameNotFoundException ,没问题。 这里给出三种解决方案(其实是两种,有两种是相同目的不同解决思路)

  1. 自定义抛出异常(这种最简单)
    在这里插入图片描述
  2. 自定义DaoAuthenticationProvider(前面讲到它是主要认证类)在JwtSecurityConfig中注册该类并注释掉认证器配置即可
    在这里插入图片描述在这里插入图片描述

在登录 (控制台抛出UsernameNotFoundException)nice
在这里插入图片描述

我们其实就是想关闭屏蔽用户名不存在异常的开关,有没有一种方式可以在spring 初始化完DaoAuthenticationProvider后调用前改变对象的属性呢?答案是有, 这就涉及到ioc初始化bean的相关知识 ,在spring ioc 给用户预留一个接口 BeanPostProcessor ioc会在实例化bean后初始化bean前和后调用该类。类似与aop 。该接口有两个方法 postProcessBeforeInitialization 前置处理器 ,postProcessAfterInitialization 后置处理器 。到这里是不是有小伙伴想到怎么做了吗?我们拦截DaoAuthenticationProvider 修改开关属性即可。
在这里插入图片描述
注意: ioc容器还有一个给用户预留的接口BeanFactoryPostProcessor 该接口会在ioc 实例化bean之前调用,我们可以实现该接口用于配置bean元数据等信息。想了解更多请看spring官方文档在这里不在展开介绍

  • 再次登录 发现也实现了抛出用户名不存在错误
    在这里插入图片描述
  • 封装错误给用户
    在这里插入图片描述

在这里插入图片描述

  • 至于密码错误你们自行拦截处理就可以了
  • 当我们登录成功后在进行访问/路径时候控制台出现
    在这里插入图片描述

这是由于我们虽然登录成功了,但是我们禁用了session 所以服务器无法判断当前登录的用户是谁导致没有认证实体

生成token

  1. 我使用java-jwt 作为token生成包 因为它简单易用
  2. 编写 tokenService
    在这里插入图片描述

在这里插入图片描述

  • 在登录入口注入tokenService 生成token
    在这里插入图片描述
    在这里插入图片描述

因为用户登录状态在token中存储在客户端,所以每次请求后台请求头携带token 后台通过自定义token过滤器拦截解析token完成认证并填充用户信息实体。

创建token解析Filter JwtDecoderFilter

在这里插入图片描述

  • 添加自定义Filter到Security
    在这里插入图片描述
  • 访问test路径 (携带登录所获取的token)
    在这里插入图片描述
    至于如何自定义授权我会在下一篇文章讲解
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qq_21602341/article/details/114577740
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢