sso单点系统

传统登录

逻辑实现

sso单点系统_第1张图片
传统登录

缺陷

仅限单台tomcat可以。集群tomcat就会出现问题,存在session共享的问题,每个系统都有自己的session,不能统一。

共享session解决方案

  1. tomcat的session复制
    优点:不需要额外开发,只需要搭建tomcat集群即可。
    缺点:tomcat 是全局session复制,集群内每个tomcat的session完全同步(也就是任何时候都完全一样的) 在大规模应用的时候,用户过多,集群内tomcat数量过多,session的全局复制会导致集群性能下降, 因此,tomcat的数量不能太多,5个以下为好。
  2. 实现单点登录系统,提供服务接口。把session数据存放在redis。
    Redis可以设置key的生存时间、访问速度快效率高。
    优点:redis存取速度快,不会出现多个节点session复制的问题。效率高。

单点登录系统流程

sso单点系统_第2张图片
单点登录系统

单点实例

portal项目(前台展示),点击登录跳转到sso系统的登录页面(login.html->login.jsp),验证账号密码之后,保存token(redis服务)到cookie(会话结束则消失),

  1. UserController
    @Autowired
    private UserService userService;
    
    //创建用户
    @RequestMapping(value="/register", method=RequestMethod.POST)
    @ResponseBody
    public TaotaoResult createUser(TbUser user) {
        
        try {
            TaotaoResult result = userService.createUser(user);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
        }
    }
    
    //用户登录
    @RequestMapping(value="/login", method=RequestMethod.POST)
    @ResponseBody
    public TaotaoResult userLogin(String username, String password,
            HttpServletRequest request, HttpServletResponse response) {
        try {
            
            TaotaoResult result = userService.userLogin(username, password, request, response);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
        }
    }
    
    @RequestMapping("/token/{token}")
    @ResponseBody
    public Object getUserByToken(@PathVariable String token, String callback) {
        TaotaoResult result = null;
        try {
            result = userService.getUserByToken(token);
        } catch (Exception e) {
            e.printStackTrace();
            result = TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
        }
        
        //判断是否为jsonp调用
        if (StringUtils.isBlank(callback)) {
            return result;
        } else {
            MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
            mappingJacksonValue.setJsonpFunction(callback);
            return mappingJacksonValue;
        }
        
    }
  1. UserServiceImpl
    public TaotaoResult userLogin(String username, String password,
            HttpServletRequest request, HttpServletResponse response) {
        
        TbUserExample example = new TbUserExample();
        Criteria criteria = example.createCriteria();
        criteria.andUsernameEqualTo(username);
        List list = userMapper.selectByExample(example);
        //如果没有此用户名
        if (null == list || list.size() == 0) {
            return TaotaoResult.build(400, "用户名或密码错误");
        }
        TbUser user = list.get(0);
        //比对密码
        if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
            return TaotaoResult.build(400, "用户名或密码错误");
        }
        //生成token
        String token = UUID.randomUUID().toString();
        //保存用户之前,把用户对象中的密码清空。
        user.setPassword(null);
        //把用户信息写入redis
        jedisClient.set(REDIS_USER_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user));
        //设置session的过期时间
        jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
        
        //添加写cookie的逻辑,cookie的有效期是关闭浏览器就失效。
        CookieUtils.setCookie(request, response, "TT_TOKEN", token);
        
        //返回token
        return TaotaoResult.ok(token);
    }

    @Override
    public TaotaoResult getUserByToken(String token) {
        
        //根据token从redis中查询用户信息
        String json = jedisClient.get(REDIS_USER_SESSION_KEY + ":" + token);
        //判断是否为空
        System.out.println(json);
        if (StringUtils.isBlank(json)) {
            return TaotaoResult.build(400, "此session已经过期,请重新登录");
        }
        //更新过期时间
        jedisClient.expire(REDIS_USER_SESSION_KEY + ":" + token, SSO_SESSION_EXPIRE);
        //返回用户信息
        return TaotaoResult.ok(JsonUtils.jsonToPojo(json, TbUser.class));
    }
  1. PageController
@Controller
@RequestMapping("/page")
public class PageController {

    @RequestMapping("/register")
    public String showRegister() {
        return "register";
    }
    @RequestMapping("/login")
    public String showLogin(String redirect, Model model) {
        model.addAttribute("redirect", redirect);
        return "login";
    }
}

redirect 为回调的url

  1. login.jsp
            doLogin:function() {
                $.post("/user/login", $("#formlogin").serialize(),function(data){
                    if (data.status == 200) {
                        alert("登录成功!");
                        if (redirectUrl == "") {
                            location.href = "http://localhost:8083";
                        } else {
                            location.href = redirectUrl;
                        }
                    } else {
                        alert("登录失败,原因是:" + data.msg);
                        $("#loginname").select();
                    }
                });
            },

cookie共享

  • domain
  • path
    cookie共享

拦截器的实现

  1. LoginInterceptor
package com.taotao.portal.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.taotao.common.utils.CookieUtils;
import com.taotao.pojo.TbUser;
import com.taotao.portal.service.UserService;
import com.taotao.portal.service.impl.UserServiceImpl;

public class LoginInterceptor implements HandlerInterceptor {
    
    @Autowired
    private UserServiceImpl userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        //在Handler执行之前处理
        //判断用户是否登录
        //从cookie中取token
        String token = CookieUtils.getCookieValue(request, "TT_TOKEN");
        //根据token换取用户信息,调用sso系统的接口。
        TbUser user = userService.getUserByToken(token);
        //取不到用户信息
        if (null == user) {
            //跳转到登录页面,把用户请求的url作为参数传递给登录页面。
            response.sendRedirect(userService.SSO_DOMAIN_BASE_USRL + userService.SSO_PAGE_LOGIN 
                    + "?redirect=" + request.getRequestURL());
            //返回false
            return false;
        }
        //取到用户信息,放行
        //把用户信息放入Request
        request.setAttribute("user", user);
        //返回值决定handler是否执行。true:执行,false:不执行。
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // handler执行之后,返回ModelAndView之前

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 返回ModelAndView之后。
        //响应用户之后。
    }
}
  1. 拦截器的配置
    
    
        
            
            
            
        
    

你可能感兴趣的:(sso单点系统)