当前位置: 首页 > Web前端 > HTML

如何做登录(单点登录)功能?

时间:2023-03-28 14:04:55 HTML

先分析一下登录的时候要干什么首先要弄清楚要干什么。登录后,系统就会知道这是谁,他有什么权限,可以给他开放哪些业务功能,可以看到哪些菜单?...这就是这个函数存在的目的和意义。如何实施?如何实现?用什么来实现的?我们的项目是Springboot+Vue前端分离型。选择使用token+redis来实现,权限使用SpringSecurity。前后端分离不可避免的问题之一就是单点登录。单点登录我们有很多实现方式:CAS中心认证、JWT、token等。我们的方式其实是基于token的单点登录实现。对于单点登录,改天再整理一篇关于OAuth2.0的实现的文章,今天就不做了。上面代码的概念越来越神秘了。让我们直接上代码。接口:@PostMapping("/login")publicAjaxResultlogin(@RequestBodyLoginBodyloginBody){AjaxResultajax=AjaxResult.success();//生成token//用户名、密码、验证码、uuidStringtoken=loginService.登录(loginBody.getUsername(),loginBody.getPassword(),loginBody.getCode(),ajax.put(Constants.TOKEN,token);spring复制用户信息返回ajax;/**登录验证*/publicStringlogin(Stringusername,Stringpassword,Stringcode,Stringuuid){//验证码开关,顺便把系统配置相关的开关缓存在redis中,系统启动时间到载入。这段代码不会发布booleancaptchaEnabled=configService.selectCaptchaEnabled();if(captchaEnabled){//uuid是验证码的rediskey,是登录页面加载时验证码生成接口返回的validateCaptcha(username,code,uuid);}//用户验证--SpringSecurityAuthenticationauthentication=null;尝试{UsernamePasswordAuthenticationTokenauthenticationToken=newUsernamePasswordAuthenticationToken(用户名,密码);AuthenticationContextHolder.setContext(authenticationToken);//该方法会去调用UserDetailsS??erviceImpl.loadUserByUsername。//authentication=authenticationManager.authenticate(authenticationToken);}catch(Exceptione){if(einstanceofBadCredentialsException){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username,Constants.LOGIN_FAIL,MessageUtils.message("user.password.not.match")));抛出新的UserPasswordNotMatchException();}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(username,Constants.LOGIN_FAIL,e.getMessage()));抛出新的ServiceException(e.getMessage());}}最后{AuthenticationContextHolder.clearContext();}AsyncManager.me().execute(AsyncFactory.recordLogininfor(username,Constants.LOGIN_SUCCESS,MessageUtils.message("user.login.success")));LoginUserloginUser=(LoginUser)authentication.getPrincipal();recordLoginInfo(loginUser.getUserId());//生成tokenreturntokenService.createToken(loginUser);}复制代码,粘贴验证验证码的部分,看看大概的逻辑(这段代码太破..不全出来)/**Verification验证码*/publicvoidvalidateCaptcha(Stringusername,Stringcode,Stringuuid){//uuid是验证码的rediskeyStringverifyKey=CacheConstants.CAPTCHA_CODE_KEY+StringUtils.nvl(uuid,"");//字符串CAPTCHA_CODE_KEY="captcha_codes:";Stringcaptcha=redisCache.getCacheObject(verifyKey);redisCache.deleteObject(verifyKey);if(captcha==null){AsyncManager.me().execute(AsyncFactory.record(username,Constants.LOGIN_FAIL,MessageUtils.message("user.jcaptcha.expire")));抛出新的CaptchaExpireException();}if(!code.equalsIgnoreCase(captcha)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username,Constants.LOGIN_FAIL,MessageUtils.message("user.jcaptcha.error")));抛出新的CaptchaException();}}在此处复制代码令牌生成部分,token/**createtoken*/publicStringcreateToken(LoginUserloginUser){Stringtoken=IdUtils.fastUUID();loginUser.setToken(令牌);setUserAgent(登录用户);refreshToken(loginUser);Mapclaims=newHashMap<>();claims.put(Constants.LOGIN_USER_KEY,token);returncreateToken(claims);}复制代码刷新令牌/**刷新订单品牌*/publicvoidrefreshToken(LoginUserloginUser){loginUser.setLoginTime(System.currentTimeMillis());loginUser.setExpireTime(loginUser.getLoginTime()+expireTime*MILLIS_MINUTE);//根据uuid缓存loginUserStringuserKey=getTokenKey(loginUser.getToken());redisCache.setCacheObject(userKey,loginUser,expireTime,TimeUnit.MINUTES);}复制代码验证令牌/**验证令牌*/publicvoidverifyToken(LoginUserloginUser){longexpireTime=loginUser.getExpireTime();longcurrentTime=System.currentTimeMillis();如果(expireTime-当前时间<=MILLIS_MINUTE_TEN){refreshToken(loginUser);}}复制代码注意,返回给前端的token其实是用JWT加密的,在SpringSecurityAnalysis的filter中实现另外,认证的时候会刷新token的有效期,见第二条注释下面的代码块。@Overrideprotectedvoidconfigure(HttpSecurityhttpSecurity)throwsException{??//...无关的代码删了??httpSecurity.addFilterBefore(corsFilter,JwtAuthenticationTokenFilter.class);}复制代码@ComponentpublicclassJwtAuthenticationTokenFilterextendsOncePerRequestFilter{??@Autowired??privateTokenServicetokenService;???@重写受保护的voiddoFilterInternal(HttpServletRequest请求,HttpServletResponse响应,FilterChain链)if(StringUtils.isNotNull(loginUser)&&StringUtils.isNull(SecurityUtils.getAuthentication())){//刷新令牌有效期tokenService.verifyToken(loginUser);UsernamePasswordAuthenticationTokenauthenticationToken=newUsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());authenticationToken.setDetails(newWebAuthenticationDetailsSource().buildDetails(请求));SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}chain.doFilter(请求,响应);两种方案都可以独立实现,两种方案都可以用于单点登录