登录
生产验证码
基本思路就是后端生成一个表达式,1+1=2
1+1=?@2
将1+1=?转换成图片,传给前端展示,把答案2放入redis
根据前端请求路径,以及前端项目部署的url,可以发现该请求其实是请求前端,但我们都知道图片信息是在后端生成的,所以这是怎么回事?
答案是这里用了反向代理,url请求前端,映射到后端,解决跨域问题,我们可以在vue.config文件查看到相关配置:
其中target是我们后端的地址。
pathRewrite的功能是
//把VUE_APP_BASE_API替换成’’,然后再映射到http://localhost:8082
然后我们可以看下后端代码
我们根据前端的请求地址中的captchaImage来找后端对应的代码,我们可以用idea自带全局搜索,双击shift打开全局搜索框即可
然后就能找到对应的controller了,在验证码后端主要处理的是
1,判断验证码是否开了
2,生成唯一标识uuid,并于固定值拼接成将要放在redis的key值
3,生成数学表达式,并把答案与表达式分开来,把答案放入uuid对应的值code中。
4,把键值对放入redis
5,把表达式写入图片流
6,用前后端交互的数据模型AjaxResult来封装uuid和图片
7,返回AjaxResult给前端
登录功能
基于之前的学习,我们想找相应功能的请求地址就很容易了,我们很快找到了
然后通过在当前项目右键点击在路径搜索/login即可找到
接下来就是自己按照断点去调试测试背后的逻辑流程了
在controller层,登录方法主要是为了生成token,然后放入前后端统一数据类AjaxResult中,返回给前端。
但里面登录方法可封装了好多层,
主要有校验验证码,通过springsecurity校验的用户名密码流程,登录成功会查询并记录ip地址,记录用户登录信息过程,根据校验成功后的Authentication来封装成一个登录实体类,根据登录实体类来生成token。
下面我们来细讲开发者设计的思想:
在校验验证码中:
首先拼接入参中的uuid,产生key值,然后从redis中找到对应的验证码答案,与入参的code值进行比较,如果code是null或者不对,会抛异常,否则就过到下面代码。
然后是校验用户名密码,该系统用springsecurity来校验,具体可以看下下面这个图
在上诉流程中,
详细的认证流程如下:
- 用户提交用户名、密码被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 过滤器获取到,
封装为请求Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。 - 然后过滤器将Authentication提交至认证管理器(AuthenticationManager)进行认证
- 认证成功后, AuthenticationManager 身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,
身份信息,细节信息,但密码通常会被移除) Authentication 实例。 - SecurityContextHolder 安全上下文容器将第3步填充了信息的 Authentication ,通过
SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。
可以看出AuthenticationManager接口(认证管理器)是认证相关的核心接口,也是发起认证的出发点,它
的实现类为ProviderManager。而Spring Security支持多种认证方式,因此ProviderManager维护着一个
List 列表,存放多种认证方式,最终实际的认证工作是由
AuthenticationProvider完成的。咱们知道web表单的对应的AuthenticationProvider实现类为
DaoAuthenticationProvider,它的内部又维护着一个UserDetailsService负责UserDetails的获取。最终
AuthenticationProvider将UserDetails填充至Authentication。
我们可以通过重写其中某类的方法来自定义对用户密码校验,但由于springsecurity流程复杂,我们一般重写UserDetelsService的loadUserByUsername方法,在这个方法中,根据用户名在数据库查找后并封装一个SysUser实体类,如果该用户为null,或者用户的状态在数据库中是已删除或者停用,都给各自抛出对应的异常。
如果用户没问题就会生成一个登录实体类LoginUser,在实例化LoginUser时候,会查询该用户的权限(与菜单相关的)并放入permision中该类的父类是UserDetails,然后返回到某个方法进行密码校验,如下图
点击查看又发现了
实质上通过match方法进行校验,这里springsecurity官方推荐使用BCryptPasswordEncoder,PasswordEncoder的实现类,这是个强哈希加密算法。不过不重写,按默认就是官方推荐的。
上诉流程如果有出问题的话,登录实现类方法会捕获异常
无论登录是否通过,都会记录登录信息,通过recordLogininfor方法来记录,该方法里面套娃了一些查询ip,并在sys_logininfor表中记录相关记录。
最后如果顺利来到最后一步生成token,该方法里面通过若依自定义随机id工具类生成token值,然后并作为key值将loginUser缓存到redis中,loginUser记录了用户所有信息,有效期为30分钟。
最后把token放入map中,然后将map通过jwt生成token并以String类型返回。