ruoyi-vue前后端分离项⽬权限控制以及登录流程详细笔记
总体流程概述:
⼀、未登录
1、打开登录页,请求被token过滤器类JwtAuthenticationTokenFilter拦截,从请求头中查询token,发现没有,接着调⽤后台⽣成验证码接⼝,⽣成验证码,将验证码值存⼊redis,然后,将uuid和验证码图⽚,响应会前端页⾯。
2、输⼊⽤户名和密码,提交表单登录请求,同样先被token过滤器类JwtAuthenticationTokenFilter拦截,从请求头中查询token,发现没有,接着执⾏后台的表单请求。根据前台的uuid,
获取redis中缓存的验证码值,如果为空,登录失败,如果验证码值和⽤户前台输⼊的码值不⼀样,登录同样失败。
3、验证码校验通过之后,开始进⾏⽤户认证登录,登录成功之后,就可以拿到⽤户信息
4、⽣成uuid,将登录的⽤户信息,缓存到redis中,并设置失效时间为30分钟
5、最后,使⽤jwt⽣成token令牌,然后将token令牌返回给前台,登录完成
⼆、已登录
1、打开某个页⾯,请求被token过滤器类JwtAuthenticationTokenFilter拦截,从请求头中获取到token。
2、使⽤jwt对token令牌进⾏解析,然后从redis中,拿到对应的⽤户信息,如果redis的失效时间和当前时间相差不⾜20分钟时,则重新刷新缓存时间为30分钟
3、最后进⾏⽤户的认证,认证通过,则可以访问页⾯
详细流程:
1、打开登录页⾯,前端发送获取验证码请求接⼝
(1)⾸先请求会被⾃定义的token过滤器拦截,即JwtAuthenticationTokenFilter extends OncePerRequestFilter ,从前端的请求头⾥⾯,获取token,判断token是否存在,⾸次登录token为空
//token过滤器验证token有效性
JwtAuthenticationTokenFilter extends OncePerRequestFilter{
.
......
LoginUser loginUser = LoginUser(request);
}
//从request中获取token
String token = Header(header);//header为令牌⾃定义标识:Authorization
//yml⽂件中配置的token信息
(2)进⼊后台⽣成验证码接⼝,将计算出的验证码值,保存到redis中,有效时间2分钟,步骤如下:
a、⽣成uuid
String uuid = IdUtils.simpleUUID();
b、⽣成redis的key
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
c、⽣成验证码图⽚以及结果
String capText = ateText();// 类似于 8*3=?@24
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@")+1);
image = ateImage(capStr);
d、将验证码图⽚的计算结果,保存到redis中,key为verifyKey,值是code,有效时间2分钟
redisCache.setCacheObject(verifyKey, code,2, TimeUnit.MINUTES);
e、返回验证码图⽚,以及uuid给前端
FastByteArrayOutputStream os =new FastByteArrayOutputStream();
ImageIO.write(image,"jpg", os);
AjaxResult ajax = AjaxResult.success();
ajax.put("uuid", uuid);
ajax.put("img", ByteArray()));
return ajax;
(3)⽤户在前端页⾯输⼊验证码值,提交表单
(4)请求依然先被token过滤器JwtAuthenticationTokenFilter拦截,此时token依然为空(5)进⼊后端登录代码,获取前端传过来的 uuid、username、password、输⼊的验证码值。
a、⾸先根据uuid,拼出redis的key,再根据key从redis中取出对应的原始验证码值,然后删掉redis中的key,
public String login(String username, String password, String code, String uuid){
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
String captcha = CacheObject(verifyKey);
redisCache.deleteObject(verifyKey);
}
b、如果从redis取出的数据是空,那么登录失败,如果从redis取出的值与前端⽤户输⼊的验证码值不相等,那么也是登录失败
if(captcha ==null)
{
<().dLogininfor(username, Constants.LOGIN_FAIL, ssage("pire")));
throw new CaptchaExpireException();
}
if(!code.equalsIgnoreCase(captcha))
{
<().dLogininfor(username, Constants.LOGIN_FAIL, ssage("")));
throw new CaptchaException();
}
c、验证码校验完之后,接下来开始进⾏⽤户的登录认证,执⾏security的⽤户认证⽅法
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
该⽅法会接着去调⽤UserDetailsServiceImpl.loadUserByUsername(String username)
d、登录成功,获取⽤户信息
<().dLogininfor(username, Constants.LOGIN_SUCCES
S, ssage("user.login.success"))); LoginUser loginUser =(LoginUser) Principal();
e、⽣成token,返回给前端
public String createToken(LoginUser loginUser)
{
String token = IdUtils.fastUUID();
loginUser.setToken(token);
//设置⽤户代理信息
setUserAgent(loginUser);
//刷新令牌有效期
refreshToken(loginUser);
Map<String, Object> claims =new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims);
}
///刷新令牌有效期:设置有效时间是30分钟,并将⽤户信息缓存到redis中
public void refreshToken(LoginUser loginUser)
{
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.LoginTime()+ expireTime * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey =Token());
//expireTime为30
redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
}
//从数据声明⽣成令牌,其中secret为⾃定义好的令牌秘钥,放在yml⽂件了,上⾯已经截图
private String createToken(Map<String, Object> claims)
{
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret)pact();
return token;
}
2、已经登录过,点击某⼀个页⾯时,后台发送的请求流程如下:
(1)⾸先请求会被⾃定义的token过滤器拦截,即JwtAuthenticationTokenFilter extends OncePerRequestFilter ,从前端的请求头⾥⾯,获取token,因为已经登录,所以此时token是有值的
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
{
LoginUser loginUser = LoginUser(request);
if(StringUtils.isNotNull(loginUser)&& StringUtils.Authentication()))
{
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginUser,null, Authorit ies());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
}
chain.doFilter(request, response);
}
}
/**
* 获取⽤户⾝份信息
el表达式获取session中的值*
* @return ⽤户信息
*/
public LoginUser getLoginUser(HttpServletRequest request)
{
// 获取请求携带的令牌
String token =getToken(request);
if(StringUtils.isNotEmpty(token))
{
Claims claims =parseToken(token);
// 解析对应的权限以及⽤户信息
String uuid =(String) (Constants.LOGIN_USER_KEY);
String userKey =getTokenKey(uuid);
LoginUser user = CacheObject(userKey);
return user;
}
return null;
}
private String getToken(HttpServletRequest request)
{
String token = Header(header);
if(StringUtils.isNotEmpty(token)&& token.startsWith(Constants.TOKEN_PREFIX))
{
token = place(Constants.TOKEN_PREFIX,"");
}
return token;
}
private Claims parseToken(String token)
{
return Jwts.parser()
.setSigningKey(secret)
.
parseClaimsJws(token)
.getBody();
}
private String getTokenKey(String uuid)
{
return Constants.LOGIN_TOKEN_KEY + uuid;
}
总结:
1、前端的每⼀次请求,后端都会通过token过滤器JwtAuthenticationTokenFilter进⾏拦截,从请求头⾥⾯获取token,然后调⽤jwt相关api接⼝,进⾏解析拿到⽤户的信息,进⾏登录认证,认证通过,则放⾏。
2、登录成功,后台⽣成token返回给前端,之后前端的每⼀次请求都会带着这个token
补充⼀下:
token过滤器需要添加到spring security 配置类⾥⾯,才能⽣效。具体如下:
package;
import Autowired;
import Bean;
import HttpMethod;
import AuthenticationManager;
import AuthenticationManagerBuilder;
import EnableGlobalMethodSecurity;
import HttpSecurity;
import WebSecurityConfigurerAdapter;
import SessionCreationPolicy;
import UserDetailsService;
import BCryptPasswordEncoder;
import UsernamePasswordAuthenticationFilter;
import LogoutFilter;
import CorsFilter;
import JwtAuthenticationTokenFilter;
import AuthenticationEntryPointImpl;
import LogoutSuccessHandlerImpl;
/
**
* spring security配置
*
* @author ruoyi
*/
@EnableGlobalMethodSecurity(prePostEnabled =true, securedEnabled =true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
/**
* ⾃定义⽤户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论