ruoyi自定义登录中ThreadLocal的使用
ruoyi自定义登录中ThreadLocal的使用 (它的ThreadLocal封装在AuthenticationContextHolder)
登录代码:
/** * 登录验证 * * @param username 用户名 * @param password 密码 * @param code 验证码 * @param uuid 唯一标识 * @return 结果 */ public String login(String username, String password, String code, String uuid) { // 验证码校验 validateCaptcha(username, code, uuid); // 登录前置校验 loginPreCheck(username, password); // 用户验证 Authentication authentication = null; try { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); AuthenticationContextHolder.setContext(authenticationToken); //ThreadLocal 存数据 // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername authentication = authenticationManager.authenticate(authenticationToken); } catch (Exception e) { if (e instanceof BadCredentialsException) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); throw new UserPasswordNotMatchException(); } else { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); throw new ServiceException(e.getMessage()); } } finally { AuthenticationContextHolder.clearContext(); //ThreadLocal 销毁 } AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); recordLoginInfo(loginUser.getUserId()); // 生成token return tokenService.createToken(loginUser); }
AuthenticationContextHolder封装的ThreadLocal代码
/** * 身份验证信息 * * @author ruoyi */ public class AuthenticationContextHolder { private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>(); public static Authentication getContext() { return contextHolder.get(); } public static void setContext(Authentication context) { contextHolder.set(context); } public static void clearContext() { contextHolder.remove(); } }
ThreadLocal确保了每个线程使用一个认证对象,不用每次使用都传递 Authentication 认证对象了,但是在本次线程完必须清除数据,
(因为spring使用的线程池,请求完不会销毁线程,回到线程池由下一个请求继续使用,防止携带'前世'数据,本次请求完需销毁)
相关视频: https://www.bilibili.com/video/BV1SJ41157oF/