分布式下如何根据token解析登录用户

token介绍

为什么需要token,浏览器向服务器发的请求为Http请求,而Http是无状态的,也就是说,服务器处理每个http请求是并不知道这个请求是那个客户端发送的。
因此需要token作为一个凭证,token是用户登录成功后,服务器为用户生成的唯一凭证,并保存token与用户对象的映射关系,也就是我们常说的session,然后服务器会将这个凭证反回到浏览器,浏览器保存这个凭证,后续每次发送请求到服务器时都会将token放置到请求头中,服务器根据token可以获取到请求是那个用户发送的

tomcat维护了一个sessionMap
在ManagerBase类中

  /**
     * The set of currently active Sessions for this Manager, keyed by
     * session identifier.
     */
    protected Map<String, Session> sessions = new ConcurrentHashMap<>();

key为session的唯一凭证,value为Session对象
在单机的情况下,tomcat替我们管理了session,在接受到http请求时,读取请求头的token,在拦截器中,取出session
并且set到HttpServletRequest的seesion中,这样我们在controller中可以直接获取到用户。

分布式情况

分布式情况下,需要做到session在多个机器上共享。tomcat维护的session已经无法满足需要了,因此一般将session存储到redis中。
具体实现

登录时记录token到redis

伪代码

public void login(String account,String pwd){
       HttpSession session = httpServletRequest.getSession();


        UserLoginResponse userLoginResponse =  userReadFacade.findByAccount(loginRequest);
        if(userLoginResponse == null){
            throw new RestException("账号活密码不正确");
        }

        if(!passwordEncoder.matches(loginRequest.getPassward(),userLoginResponse.getPassward())) {
         throw new RestException("账号活密码不正确");
        }
       session.setAttribute("user",userDTO);//设置用户对象
}

实现tomcat拦截器

请求处理后保存session到resis

public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter{
@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);

		SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
		SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,
				response);

		try {
			filterChain.doFilter(wrappedRequest, wrappedResponse);
		}
		finally {
			wrappedRequest.commitSession();//当请求结束后,session可能会发生变化,需要保存session到redis
		}
	}
}

image.png
key为sessionId,value为session对象
sessionId也就是客户端的token

请求前获取session

伪代码
可以重写httpRequest的getSession方法,从redis中获取

	private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
    	@Override
		public HttpSessionWrapper getSession(boolrean create) {
			
			S session = SessionRepositoryFilter.this.sessionRepository.findById(sessionId);
					if (session != null) {
						this.requestedSession = session;
						this.requestedSessionId = sessionId;
						break;
					}
		    return session;
		}
}

设置用户信息到ThreadLocal

为了获取用户信息方法,经常将用户信息设置到threadLocal中,后续不需要传参
两种方式

1.使用springMvc拦截器

public class PageInterceptor implements HandlerInterceptor {
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
           SsoSecUser ssoSecUser;
              
                ssoSecUser = (SsoSecUser)request.getSession().getAttribute("login_user");
                      if (ssoSecUser == null) {
                        Authentication.setSecUser((SsoSecUser)null);
                        return true;
                    } else {
                        Authentication.setSecUser(ssoSecUser);
                        return true;
                    }
                }
     }   
}

2.使用AOP切面拦截Controller,获取request中的session

@Aspect
@Slf4j
@Order(3)
public class UserContextAop {

    @Pointcut("execution(public * com.xishan.store.usercenter.userweb.controller.*.*(..))")
    private void userContextAspect() {
    }

    @Around("userContextAspect()")
    public Object webContextAround(ProceedingJoinPoint point) throws Throwable {
        //执行前,塞进UserContext中,执行后清除UserContext
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        UserDTO user = null;
        if( request.getSession().getAttribute("user") != null){
            user = (UserDTO) request.getSession().getAttribute("user");
        }
        if(user != null){
            UserContext.putCurrentUser(user);
            RpcContext.getContext().setAttachment("user",JSON.toJSONString(user));
        }
        Object result = null;
        try {
            result = point.proceed();//可能抛出异常,并且异常不能被吃了
        }catch (Throwable e){
            throw  e;
        }finally {
            UserContext.clearCurrentUser();
            RpcContext.getContext().removeAttachment("user");
        }
        return result;
        //执行目标
    }
}

总结

使用缓存session的方法,往往只有在登录的时候,才会将session存起来。那么加入后续用户对象更新,那么session的用户信息就不是最新的了,这也是往往我们更改用户信息需要重新登录的原因

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Node.js基于token登录验证是一种常见的身份验证和授权机制。它使用令牌作为凭据来验证用户身份并授权访问受保护的资源。 当用户登录成功后,服务器会生成一个token。这个token是一个随机字符串,它包含了一些用户信息和相关权限。服务器将这个token发送给客户端,客户端将其保存在本地,如localStorage或cookie中。 当用户访问需要身份验证的资源时,客户端将token作为请求头或请求参数发送给服务器。服务器接收到请求后,会解析token并验证其有效性。若token有效且未过期,则认为用户登录,可以授权用户访问受保护的资源。否则,服务器会返回未授权的错误响应。 优点: 1. 无状态:服务器不需要保存用户登录状态,因此可以更好地扩展和分布式。 2. 安全性:通信过程中token被加密和签名,使其难以伪造和改变。 3. 高效性:验证过程简单,减少了服务器的负载。 4. 可扩展性:可以轻松地用于支持多个客户端和API。 缺点: 1. 客户端存储:客户端需要存储token,如果被盗用或泄露,可能会导致安全风险。 2. 无法废止tokens:一旦发出的token被盗用,服务器无法废止,除非过期时间到期或用户进行密码重置。 总之,基于token登录验证是一种简单且高效的身份验证和授权机制,适用于大多数Web应用程序。但在实施时,必须注意安全风险,例如使用HTTPS来确保通信安全,并定期更新token以减少盗用的风险。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值