sso单点登录

sso概念

什么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分 (即:一个账户登录访问多个子系统)

1. 登录模块说明

    相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。这个过程,也就是单点登录的原理,如图:

这里写图片描述

2.注销模块说明

单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁,如图:

这里写图片描述

3.整体sso认证流程图
这里写图片描述

4.实现

sso采用客户端/服务端架构,先看sso-client与sso-server要实现的功能(sso认证中心=sso-server)

sso-client

    1.拦截子系统未登录用户请求,跳转至sso认证中心
    2.接收并存储sso认证中心发送的令牌
    3.与sso-server通信,校验令牌的有效性
    4.建立局部会话
    5.拦截用户注销请求,向sso认证中心发送注销请求
    6.接收sso认证中心发出的注销请求,销毁局部会话

sso-server

    1.验证用户的登录信息
    2.创建全局会话
    3.创建授权令牌
    4.与sso-client通信发送令牌
    5.校验sso-client令牌有效性
    6.系统注册
    7.接收sso-client注销请求,注销所有会话

sso-server关键代码

@WebServlet("/login")
public class UserLoginServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private UserAccountService userAccountService = new UserAccountService();

    /**
     * 进入登录页
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 获得原始请求的url并保存传递,当登录成功时,让浏览器再次跳转到这个url
        String origUrl = req.getParameter("origUrl");
        req.setAttribute("origUrl", origUrl);

        req.getRequestDispatcher("/WEB-INF/view/login.jsp").forward(req, resp);
    }

    /**
     * 登录表单提交时处理
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String account = request.getParameter("account");
        String passwd = request.getParameter("passwd");

        String origUrl = request.getParameter("origUrl");

        // 按account查找用户
        User user = null;
        try {
            user = userAccountService.findUserByAccount(account);

            if (user != null) {
                if (user.getPasswd().equals(passwd)) { // 判断密码是否正确
                    // 1 生成token
                    String token = KeyGenerator.generate();
                    // 2 将token user存储到全局唯一数据结构中
                    TokenUserData.addToken(token, user);
                    // 3 写cookie JVM
                    Cookie tokenCookie = new Cookie("token", token);
                    tokenCookie.setPath("/");
                    // tokenCookie.setDomain(DOMAIN);
                    tokenCookie.setHttpOnly(true);
                    response.addCookie(tokenCookie);

                    // 4 跳转到原请求
                    if (StringUtil.isEmpty(origUrl)) {
                        origUrl = "login_success";
                    } else {
                        origUrl = URLDecoder.decode(origUrl, "utf-8");
                    }
                    response.sendRedirect(origUrl);
                } else {
                    backToLoginPage(request, response, account, origUrl, "密码不正确");
                }
            } else { // 用户不存在
                backToLoginPage(request, response, account, origUrl, "用户不存在");
            }

        } catch (SQLException e) {
            e.printStackTrace();
            backToLoginPage(request, response, account, origUrl, "发生系统错误!");
        }

    }

    /**
     * 登录错误返回的信息
     * 
     * @param req
     * @param resp
     * @param account
     * @param origUrl
     * @param errInfo
     * @throws ServletException
     * @throws IOException
     */
    private void backToLoginPage(HttpServletRequest req, HttpServletResponse resp, String account, String origUrl,
            String errInfo) throws ServletException, IOException {

        req.setAttribute("account", account);
        req.setAttribute("origUrl", origUrl);
        req.setAttribute("errInfo", errInfo);

        req.getRequestDispatcher("/WEB-INF/view/login.jsp").forward(req, resp);
    }

sso-client关键代码

public class SSOFilter implements Filter {

    // SSO Server登录页面URL
    private static final String SSO_LOGIN_URL = "/server/login",
            SSO_VALIDATE_URL = "http://localhost:8080/server/validate";

    // 拦截操作
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        // 从请求中提取token
        String token = CookieUtil.getCookie(req, "token");

        // 本次请求的完整路径
        String origUrl = req.getRequestURL().toString();
        String queryStr = req.getQueryString();
        if (queryStr != null) {
            origUrl += "?" + queryStr;
        }

        // token 不存在,跳转到SSOServer用户登录页
        if (token == null) {
            resp.sendRedirect(SSO_LOGIN_URL + "?origUrl="
                    + URLEncoder.encode(origUrl, "utf-8"));
        } else { // token存在,验证有效性
            URL validateUrl = new URL(SSO_VALIDATE_URL + "?token=" + token);
            HttpURLConnection conn = (HttpURLConnection) validateUrl
                    .openConnection();

            conn.connect();
            InputStream is = conn.getInputStream();

            byte[] buffer = new byte[is.available()];
            is.read(buffer);

            String ret = new String(buffer);

            if (ret.length() == 0) { // 返回空字符串,表示 token无效
                resp.sendRedirect(SSO_LOGIN_URL + "?origUrl="
                        + URLEncoder.encode(origUrl, "utf-8"));
            } else {
                String[] tmp = ret.split(";");
                User user = new User();
                for (int i = 0; i < tmp.length; ++i) {
                    String[] attrs = tmp[i].split("=");
                    switch (attrs[0]) {
                    case "id":
                        user.setId(Integer.parseInt(attrs[1]));
                        break;
                    case "name":
                        user.setName(attrs[1]);
                        break;
                    case "account":
                        user.setAccount(attrs[1]);
                        break;
                    }
                }
                request.setAttribute("user", user);
                chain.doFilter(request, response);
            }
        }
    }

参考说明:http://www.importnew.com/22863.html?replytocom=541115#respond
极客学院

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值