互联网应用App 中单机和分布式应用的登录校验解决方案

单机的Tomcat应用登录校验:

  用户首次登录成功后,服务端会创建一个Session会话,客户端会生成一个sessionid,客户端会把sessionid保存到cookie里,每次请求都携带这个sessionid,服务端通过校验来判断是拦截还是放行

分布式应用中Session共享登录校验:

  真实的应用不可能单节点部署,所以就有多个节点登录session共享的问题需要解决。tomcat支持session共享,但是有广播风暴;尤其用户量大的时候,占用资源非常严重

推荐使用Redis来存储token:

  服务端使用UUID生成随机64位或者128位token,放入redis中,然后返回给客户端并存储在cookie中,用户每次访问都携带此token,服务端去redis中校验是否有此用户即可

分布式应用登录校验解决方案Json Web Token

  Jwt是一个开放标准,他定义了一种用于简洁,自包含的用于通信双方之间以Json对象的形式安全传递信息的方法,可以使用HMAC算法或者RSA的公钥对其签名

简单来说,就是通过一定规范生成token,然后通过解密算法逆向解密,就可以获得用户信息

Json Web Token的封装通用方法,JwtTokenUtil

public class JwtTokenUtil {

	public static final String TOKEN_HEADER = "Authorization";// token header
	public static final String TOKEN_PREFIX = "Bearer ";
	private static final String SECRET = "gkadmin";// 密钥
	private static final String ISS = "gkadmin";
	private static final String ROLE_CLAIMS = "claims";
	private static final long EXPIRATION = 12*3600L;// 3600L;// 时间

	//创建token
	public static String createToken(String username) {
		String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS512, SECRET).claim(ROLE_CLAIMS, username)
				.setSubject(username).setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000)).compact();
		return jwt;
	}

	public static String getusername(String token) {
		return getTokenBody(token).getSubject();
	}

	// 判断token是否已过期
	public static boolean isExpiration(String token) {
		try {
			return getTokenBody(token).getExpiration().before(new Date());
		} catch (ExpiredJwtException e) {
			return true;
		}
	}

	public static Claims getTokenBody(String token) {
		return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
	}

}

新建一个拦截器类,实现HandlerInterceptor接口,注入Bean,UserService


    /*
    * 进入到系统逻辑之前要执行的方法
    * */
    public class LoginInterceptor implements HandlerInterceptor {
    
        @Resource
        private  UserService userservice;

 在重写的preHandle方法里获取token并解密,如果解析不到,通过工具类反馈给客户端无权限的提示,如果token过期,提示“重新登录”

 @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            String accesToken = request.getHeader("token");
            if(accesToken == null){
                accesToken = request.getParameter("token");
            }
            if(!StringUtil.isEmpty(accesToken)){
                Claims claims = JwtTokenUtil.getTokenBody(accesToken);
                String username = (String) claims.get("username");
                List<UserInfo> info = userservice.findByToken(username);
                 if (info.size()==0){
                     sendJsonMessage(response,JsonData.buildError("无权限访问,请重新登录"));
                     return false;
                 }
                if (JwtTokenUtil.isExpiration(accesToken)){
                    sendJsonMessage(response,JsonData.buildError("身份已失效,请重新登录"));
                    return false;
                }
                return true;
            }
        }catch (Exception e){}
        sendJsonMessage(response, JsonData.buildError("身份有误,请重新登录"));
        return false;
    }

 新建Config类实现WebMvcConfigurer接口,注入拦截器bean,在addInterceptors里配置拦截路径,调用excludePathPatterns方法放行需要放行的资源

 @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor()).addPathPatterns("/api/pro/**")
                .excludePathPatterns("/api/pro/register","/api/pro/login");
        WebMvcConfigurer.super.addInterceptors(registry);
    }

新建LoginController,实现注册方法

@RequestMapping("register")
    public HttpResult getUsers(@RequestBody UserInfo user) {
        UserInfo uo = new UserInfo();
        //对传进来的用户密码进行MD5加密
        uo.setPassword(MD5.md5(user.getPassword()));
        int i =  userservice.saveUserInfo(user);
        if (i == 0){
           return HttpResult.error("注册失败,未知异常");
        }
        return HttpResult.ok("注册成功,请登录");
    }

 实现Login方法和测试用的update方法

@RequestMapping("login")
    public HttpResult loginUsers(@RequestBody UserInfo user){
      String token = userservice.getUserAll(user);
          if (token == null || "".equals(token)){
             return HttpResult.error("账号密码错误,请重新登录");
          }
        return HttpResult.ok(token);
    }

    @RequestMapping("update")
    public HttpResult updateUsers(){
        System.out.println("Hello,world!");
        return HttpResult.ok("访问成功");
    }

接下来用Postman工具进行测试,发送Json数据到后台,成功响应

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbWFzdGVyQ29kZV93eQ==,size_17,color_FFFFFF,t_70,g_se,x_16

 测试登录,登录响应成功并返回了token密钥

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbWFzdGVyQ29kZV93eQ==,size_20,color_FFFFFF,t_70,g_se,x_16

   我们测试客户端携带token,访问update。由于update方法路径我没有配置放行,所以请求会被拦截,在拦截中校验token,判断是否放行

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbWFzdGVyQ29kZV93eQ==,size_20,color_FFFFFF,t_70,g_se,x_16

 我们模拟黑客拦截获取到token并进行篡改,再访问,看后台如何响应

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbWFzdGVyQ29kZV93eQ==,size_20,color_FFFFFF,t_70,g_se,x_16

  大型互联网App 用户人数上亿,如抖音快手微信等App,给数据库带来的瞬间并发和后台登录压力可想而知。所以大型项目都采用前后端分离,服务器分布式集群部署,这样可以把压力分散到各个节点,每个服务器节点只负责处理一部分的功能

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值