单点登录的解决方案(Session)

以前,做过的项目中有单点登录的模块,以前做的模块没仔细观察,查看资料发现单点登录的解决方案还挺多!以下就记录单点的不同解决方案;

 http协议的特性

无状态的,就是用户连接只要获取响应,服务器不记录用户状态,就算用户访问100次,服务器也不知道用户具体信息;

    1.集群环境下的session共享问题

通过cookie+session可以保存用户状态;

     2.关于负载均衡算法分析

         2.1)轮询,加权轮询

        2.2)Hash算法(可以解决session共享问题)

        2.3)随机

        2.4)最小连接数

     3.Session共享问题的解决办法

扫描二维码关注公众号,回复: 4194526 查看本文章

         3.1)Session replication Session复制 配置文件 (tomcat中可以配置)  

        使得集群中各个服务器相互保存各自节点的session数据,tomcat本身就可以实现session复制的功能,基于IP组播的方式,同步时候性能会很低;出现问题:

            3.1.1)随着集群的数量越来越大,session带来的带宽影响网络开销

            3.1.2)每个节点都要存储集群中所有节点的session数据,很耗费内存

      3.2) Cookie Based  Token(JWT)(通行证)

      基于服务的一定算法,生成一个token给客户端,客户端每次清楚,都携带这个token服务器验证token是否有效,再对token的关键字进行处理(一种纯cookie的方式);JWT强调的是服务器端不对token进行存储,而是通过签名算法进行解密.

      3.3) Session的统一管理  Redis存储或者mysql存储(把jsessio存储到同一个位置),无论哪个节点新增和修改了session,最终都发生在存储的地方;需要双击热备份,防止突然宕机  出现的问题:

           3.3.1)session数据进行网络操作,存在延迟性

           3.3.2) 如果session服务器宕机,将大规模影响到应用

    3.4) Session sticky (会话粘性)  利用hash算法可以解决session sticky的问题。这种解决方案会出现的问题:

          3.4.1)有一台服务器宕机,这台机器上保存的session会丢失

          3.4.2)这种方式实现了session保存,没有办法在四层进行网络转发,只能在7层进行网络转发

4.单点登录实现方案

利用jwt token的方式

Jwt token三部分组成,头部(header),有效载荷(playload),签名(signature)

附录:

jwt 实际上就是定义了一套数据加密以及验签的算法的规范,根据这个规范来实现单点登录,以及数据传输及验签功能。

   问题:1)不能传递敏感问题

              2)jwt中的部分内容可以解密破解

利用token来解决session共享问题解决方案的核心代码:

1.TokenIntercepter.java

import java.lang.reflect.Method;

public class TokenIntercepter extends HandlerInterceptorAdapter {

    private final String ACCESS_TOKEN="access_token";

    @Autowired
    IUserCoreService iUserCoreService;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        if(!(handler instanceof HandlerMethod)){
            return true;
        }

        HandlerMethod handlerMethod=(HandlerMethod)handler;
        Object bean=handlerMethod.getBean();

        if(isAnoymous(handlerMethod)){
            return true;
        }
        if(!(bean instanceof BaseController)){
            throw new RuntimeException("must extend basecontroller");
        }
        // 利用CookieUitl工具类取出cookie的值
        String token=CookieUtil.getCookieValue(request,ACCESS_TOKEN);
        // 判断request请求是不是Ajax
        boolean isAjax=CookieUtil.isAjax(request);
        if(StringUtils.isEmpty(token)){ //如果为空
            if(isAjax){ // 设置返回值
                response.setContentType("text/html;charset=UTF-8");
                response.getWriter().write("{\"code\":\"-1\",\"msg\":\"error\"}");
                return false;
            }
            response.sendRedirect(GpmallWebConstant.GPMALL_SSO_ACCESS_URL); //并重定向到sso_access_url链接
            return false;
        }
        CheckAuthRequest checkAuthRequest=new CheckAuthRequest();
        checkAuthRequest.setToken(token);
        CheckAuthResponse checkAuthResponse=iUserCoreService.validToken(checkAuthRequest);
        if("000000".equals(checkAuthResponse.getCode())){
            BaseController baseController=(BaseController)bean;
            baseController.setUid(checkAuthResponse.getUid());
            return super.preHandle(request, response, handler);
        }
        if(isAjax){
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write("{\"code\":\""+checkAuthResponse.getCode()+"\"" +
                    ",\"msg\":\""+checkAuthResponse.getMsg()+"\"}");
            return false;
        }
        response.sendRedirect(GpmallWebConstant.GPMALL_SSO_ACCESS_URL);
        return false;
    }

    private boolean isAnoymous(HandlerMethod handlerMethod){
        Object bean=handlerMethod.getBean();
        Class clazz=bean.getClass();
        if(clazz.getAnnotation(Anoymous.class)!=null){
            return true;
        }
        Method method=handlerMethod.getMethod();
        return method.getAnnotation(Anoymous.class)!=null;
    }
}

2.BaseController.java

public class BaseController {

    static  ThreadLocal<String> uidThreadLocal=new ThreadLocal<>();

    public void setUid(String uid){
        uidThreadLocal.set(uid);
    }
    public String getUid(){
      return  uidThreadLocal.get();
    }

}

3.UserContoller.java

@RestController
public class UserController extends BaseController{

    @Autowired
    IUserCoreService userCoreService;

    @Autowired
    KafkaTemplate kafkaTemplate;

    @Anoymous
    @PostMapping("/login")
    public ResponseData doLogin(String username, String password,
                                     HttpServletResponse response){
        ResponseData data=new ResponseData();
        UserLoginRequest request=new UserLoginRequest();
        request.setPassword(password);
        request.setUserName(username);
        UserLoginResponse userLoginResponse=userCoreService.login(request);
        response.addHeader("Set-Cookie",
                "access_token="+userLoginResponse.getToken()+";Path=/;HttpOnly");

        data.setMessage(userLoginResponse.getMsg());
        data.setCode(userLoginResponse.getCode());
        data.setData(GpmallWebConstant.GPMALL_ACTIVITY_ACCESS_URL);
        return data;
    }


    @GetMapping("/register")
    @Anoymous
    public @ResponseBody
    ResponseData register(String username, String password, String mobile){
        ResponseData data=new ResponseData();

        UserRegisterRequest request=new UserRegisterRequest();
        request.setMobile(mobile);
        request.setUsername(username);
        request.setPassword(password);
        try {
            UserRegisterResponse response = userCoreService.register(request);
            //异步化解耦
            kafkaTemplate.send("test",response.getUid());
            data.setMessage(response.getMsg());
            data.setCode(response.getCode());
        }catch(Exception e) {
            data.setMessage(ResponseEnum.FAILED.getMsg());
            data.setCode(ResponseEnum.FAILED.getCode());
        }
        return data;
    }
}

猜你喜欢

转载自blog.csdn.net/zy345293721/article/details/83857319