单点登录简单实现

什么是单点登录

单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录。

实现机制

当用户第一次访问应用系统的时候,因为还没有登录,会被引导到SSO的认证系统中进行登录;根据用户提供的登录信息,认证系统进行身份校验,如果通过校验,应该返回给用户一个认证的凭据--token;用户再访问别的应用的时候,就会将这个token带上,作为自己认证的凭据,应用系统接受到请求之后会把token送到认证系统进行校验,检查toke的合法性。如果通过校验,用户就可以在不用再次登录的情况下访问应用系统2和应用系统3了。

实现流程

这里写图片描述

代码实现

sso子系统登录拦截器

当子系统进行操作是(比如需要订单系统提交订单时)时必须要求用户登录,可以使用拦截器来实现。拦截器的处理流程如下:
1、拦截请求url
2、从cookie中取token
3、如果没有toke跳转到登录页面。
4、取到token,需要调用sso系统的服务查询用户信息。
5、如果用户session已经过期,跳转到登录页面
6、如果没有过期,放行。

拦截器配置

<!-- 配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- <mvc:mapping path="/order/**"/> -->
            <mvc:mapping path="/item/**"/>
            <bean class="com.taotao.portal.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

拦截器实现

/**
 * 用户登录拦截器
    * @ClassName: LoginInterceptor
    * @Description: TODO(这里用一句话描述这个类的作用)
    * @author Lenovo
    * @date 2018年8月30日
    *
 */

public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;
    @Value("${SSO_LOGIN_URL}")
    private String SSO_LOGIN_URL;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {
        // 1、拦截请求url
        // 2、从cookie中取token
        String token = CookieUtils.getCookieValue(request, "TT_TOKEN");
        // 3、如果没有toke跳转到登录页面。
        if(StringUtils.isEmpty(token)){
                return null;
        }
        // 4、取到token,需要调用sso系统的服务查询用户信息。
        String json = HttpClientUtil.doGet(SSO_BASE_URL + SSO_USER_TOKEN_SERVICE + token);
        //吧json转换成java对象
        TaotaoResult result = TaotaoResult.format(json);
        if(result.getStatus() != 200){
            return null;
        }
        //取用户对象
        TaotaoResult.formatToPojo(json, TbUser.class);
        TbUser user = (TbUser) result.getData();
        // 5、如果用户session已经过期,跳转到登录页面
        if (user == null) {
            //跳转至sso认证中心;登录页url+回调参数:比如在付款页付款是调到登录也,登录后再根据回调函数,调到付款页
            response.sendRedirect(SSO_LOGIN_URL + "?redirectURL=" + request.getRequestURI());
            return false;
        }
        // 6、如果没有过期,放行。
        return true;
    }


    //执行handler之后,但是再返回model和view之前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
            Object arhandlerg2, ModelAndView modelAndView) throws Exception {
        // TODO Auto-generated method stub

    }


    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

    }

}

注意:
1、在第三步“3、如果没有toke跳转到登录页面。”处,会跳转到接下来要分享的用户登录,如果存在token,会进到接下来要分享的通过token查询用户信息进行验证。
2、在第五步调到登录页面时,需要附带上拦截器拦截的url,以便登录成功后,直接返回之前的界面。

用户登录

用户登录protal系统时,跳转到用户登录页面,sso验证中心接受用户名和密码,并检验,如果密码正确,生成用户认证凭据-token,并把用户信息写入redis保存在sso服务端,然后把登录成功,并把token返回给客户端,并写入cookie。

@Autowired
    private TbUserMapper userMapper;
    @Autowired
    private JedisClient jedisClient;

    @Value("${REDIS_SESSION_KEY}")
    private String REDIS_SESSION_KEY;
    @Value("${SESSION_EXPIRE}")
    private Integer SESSION_EXPIRE;

    /**
     * 用户登录
     */
    @Override
    public TaotaoResult login(String username, String password,
            HttpServletRequest request, HttpServletResponse response) {
        // 校验用户名和密码是否正确
        TbUserExample example = new TbUserExample();
        Criteria criteria = example.createCriteria();
        criteria.andUsernameEqualTo(username);
        List<TbUser> list = userMapper.selectByExample(example);
        //取用户信息
        if(list == null || list.isEmpty()){
            return TaotaoResult.build(400, "用户名或密码错误");
        }
        TbUser user = list.get(0);
        //检验密码
         if(user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes()))){
             return TaotaoResult.build(400, "用户名或密码错误!");            
         }
         //登录成功
         //生成token,token即为授权令牌
         String token = UUID.randomUUID().toString();
         //吧用户信息写入redis
         user.setPassword(null);
         jedisClient.set(REDIS_SESSION_KEY + ":" + token, JsonUtils.objectToJson(user));
         //设置session的过期时间
         jedisClient.expire(REDIS_SESSION_KEY + ":" + token, SESSION_EXPIRE);
         //写cookie,浏览器关闭,cookie失效
         CookieUtils.setCookie(request, response, "TT_TOKEN", token);
         return TaotaoResult.ok(token);

    }

将token存储在redis中,是因为redis可以为key设置有效时间也就是令牌的有效期。redis运行在内存中,速度非常快,正好sso-server不需要持久化任何数据。

通过token查询用户信息

订单服务需要查询订单时,先从cookie中取token,并调用sso认证中心根据token到reids查询用户信息,如果用户信息不存在说明session已经过期,返回400并提示用户session已经过期,接着会跳转到sso的登录界面。如果查询到用户,返回用户信息,并且更新一下用户的过期时间。

@Autowired
private JedisClient jedisClient;

@Value("${REDIS_SESSION_KEY}")
private String REDIS_SESSION_KEY;
@Value("${SESSION_EXPIRE}")
private Integer SESSION_EXPIRE;

@Override
    public TaotaoResult getUserByToken(String token) {
        // 根据token取用户信息
        String json = jedisClient.get(REDIS_SESSION_KEY + ":" + token);
        //判断是否查询到结果
        if (StringUtils.isEmpty(json)) {
            return TaotaoResult.build(400, "用户session已经过期");
        }
        //把json转换成java对象
        TbUser user = JsonUtils.jsonToPojo(json, TbUser.class);
        //更新session的过期时间
        jedisClient.expire(REDIS_SESSION_KEY + ":" + token, SESSION_EXPIRE);

        return TaotaoResult.ok(user);
    }

核心步骤

1、用户未登录时访问子站一,子站一服务器通过拦截器检测到用户没登录(没有本站session,因为没传过来session对应cookie),于是通知浏览器跳转到登录界面,并在跳转的URL参数中带上当前页面地址,以便登录后自动跳转回本页。
2、显示登录界面后,用户提交登录请求到服务端,服务端验证通过,创建和账号对应的用户登录凭据(token),并存储在key-value数据库(比如redis)。
然后,服务端通知浏览器把该token作为SSO服务站点的cookie存储起来,并跳转回子站一,跳回子站一的URL参数中会带上这个token。

/**
     * 展示登录页面
     */
    @RequestMapping("/page/login")
    public String showLogin(String redirectURL,Model model) {
        //需要把参数传递给jsp
        model.addAttribute("redirect", redirectURL);
        return "login";
    }

3、跳转回子站一后,子站一服务端检测到浏览器请求的URL中带了回调参数,就会跳转到相应页面。
4、用户再访问子站二。子站二服务器检测到用户没登录,于是通知浏览器跳转到SSO服务站点。
5、浏览器访问SSO服务站点时会带上上述2环节创建的token这个cookie。SSO服务站点根据该token能找到对应用户,于是通知浏览器跳转回子站二,并在跳转回去的URL参数中带上这个token。
6、子站二服务端检测到浏览器请求的URL中带上了回调参数,于是又会走上述3对应步骤,跳入登录前请求的页面。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木子松的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值