JSP+Servlet+HttpClient实现单点登录教程(二)

Server实现

1、页面我们仿照慕课网的页面进行设计,这里已经准备好了登录页面,将login.jsp拷贝到SSO_Server里的views里面。

2、在com.jikewenku.sso.servlet包下面新建一个LoginServelet.java

package com.jikewenku.sso.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.UUID;


public class LoginServlet extends HttpServlet {

    private String domains;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        domains = config.getInitParameter("domains");
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //此语句表示当request.getServletPath()的值是/login时就是登录操作
        if (Objects.equals("/login", request.getServletPath())) {
            String username = request.getParameter("username");
            String password = request.getParameter("password");

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

            if (null == source || Objects.equals("", source)) {
                String referer = request.getHeader("referer");
                source = referer.substring(referer.indexOf("source=") + 7);
            }

            if (Objects.equals(username, password)) {
                //使用UUID.randomUUID().toString()会得到一个由字母数组组成,用-链接的随机字符串,使用replace()方法去掉
                String ticket = UUID.randomUUID().toString().replace("-", "");
                System.out.println("******************************:" + ticket);
                response.sendRedirect(source + "/main?ticket=" + ticket + "&domains=" +
                        domains.replace(source + ",", "").replace("," + source, "").replace(source, ""));
            } else {
                request.setAttribute("source", source);
                request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
            }
        } else if (Objects.equals("/ssoLogin", request.getServletPath())) {
            request.getRequestDispatcher("/WEB-INF/views/login.jsp").forward(request, response);
        } else if (Objects.equals("/ssoLogout", request.getServletPath())) {
            String source = request.getParameter("source");

            //由于用户信息没有存储在内存或者类似memcache这样的缓存中,所以这里没有相关的助理
            //在ssoLogout请求时,传过来当前的用户名,根据用户名查找内存或者缓存,删除相应信息,以完成退出
            //用户从哪来?在实行ssoLogin时返回的ticket中,要包含用户的信息(能标识用户唯一性即可,uuid也可以,只是需要在sso的server中记录一下这个uuid和用户的对应关系)
            //webapp1或者webapp2在调用ssoLogout时把ticket传回来即可

            if (null == source || Objects.equals("", source)) {
                String referer = request.getHeader("referer");
                source = referer.substring(referer.indexOf("source=") + 7);
            }

            response.sendRedirect(source + "/logout?domains=" +
                    domains.replace(source + ",", "").replace("," + source, "").replace(source, ""));
        }

    }
}

3、然后对我们的web.xml进行配置



    
        LoginServlet
        com.jikewenku.sso.servlet.LoginServlet
        
            domains
            http://127.0.0.1:8081,http://127.0.0.1:8082
        
    
    
        LoginServlet
        /login
    

    
        LoginServlet
        /ssoLogin
    

    
        LoginServlet
        /ssoLogout
    

SSO基本功能实现及验证

接下来我们将完成以下任务:

1、使用过滤器实现对web应用登录的处理

2、使用Servlet完成演示中实现的各种跳转及页面请求处理

3、使用线程实现cookie的添加

具体实现

创建一个UserFilter实现Filter接口并覆盖Filter的方法,完成以下编码

package com.jikewenku.sso.filter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;


public class UserFilter implements Filter {

    private String server;

    private String app;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        server = filterConfig.getInitParameter("server");
        app = filterConfig.getInitParameter("app");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        if (Objects.equals("/ssoLogout", ((HttpServletRequest) request).getServletPath())) {
            ((HttpServletResponse) response).sendRedirect(server + "/ssoLogout?source=" + app);
            return;
        }

        String ticket = null;
        if (null != ((HttpServletRequest) request).getCookies()) {
            for (Cookie cookie : ((HttpServletRequest) request).getCookies()) {
                if (Objects.equals(cookie.getName(), "Ticket_Granting_Ticket")) {
                    ticket = cookie.getValue();
                    break;
                }
            }
        }

        if (!Objects.equals(null, ticket)) {
            //判断超时时间
            String[] values = ticket.split(":");
            ticket = request.getParameter("ticket");
            if (Long.valueOf(values[1]) < System.currentTimeMillis()) {//超时
                if (Objects.equals(null, ticket)) {
                    ((HttpServletResponse) response).sendRedirect(server + "/ssoLogin?source=" + app);
                    return;
                } else {
                    ticket = ticket + ":" + (System.currentTimeMillis() + 10000);
                    ((HttpServletResponse) response).addCookie(new Cookie("Ticket_Granting_Ticket", ticket));
                    filterChain.doFilter(request, response);
                    return;
                }
            }
            //应该进行用户校验,如果不是用户或用户非法,需要跳转到登录页面或者不需要登录的页面
            filterChain.doFilter(request, response);
            return;
        }

        ticket = request.getParameter("ticket");
        //此处实现的功能是:如果ticket为空,就转到登录页面
        if (!Objects.equals(null, ticket) && !Objects.equals("", ticket.trim())) {
            ticket = ticket + ":" + (System.currentTimeMillis() + 10000);
            ((HttpServletResponse) response).addCookie(new Cookie("Ticket_Granting_Ticket", ticket));
            filterChain.doFilter(request, response);
        } else {
            ((HttpServletResponse) response).sendRedirect(server + "/ssoLogin?source=" + app);
        }
    }

    @Override
    public void destroy() {

    }
}

编写web.xml



    
        UsreFilger
        com.jikewenku.sso.filter.UserFilter
        
            server
            http://127.0.0.1:8080
        
        
            app
            http://127.0.0.1:8081
        
    
   
        UsreFilger
        /*
    


在WebApp1中的servlet包下创建一个MainServlet继承HTTPServlet,然后完成下面的编码

package com.jikewenku.sso.servlet;


import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * TODO {file desc}
 */
public class MainServlet extends HttpServlet {

    private ExecutorService service = Executors.newFixedThreadPool(10);

    private String servers;

    private void syncCookie(String server, String ticket, String method) {
        //submit方法的作用是向线程池提交一个Runable任务用于执行
        service.submit(new Runnable() {
            @Override
            public void run() {
                //这句话的作用是:创建请求方法的实例,并制定URL。
                // 如果需要发送POST请求,就创建HTTPPost对象
                HttpPost httpPost = new HttpPost(server + "/" + method + "?ticket=" + ticket);
                CloseableHttpClient httpClient = null;
                CloseableHttpResponse response = null;
                try {
                    httpClient = HttpClients.createDefault();
                    //将请求发出去了,就会触发另一个应用的Servlet中的
                    // servletPath为/setCookie的代码的执行,
                    // 也就是为另一个应用添加了cookie
                    response = httpClient.execute(httpPost);
                    //此处的entity就是添加cookie的同时存放到response中的ok,
                    //得到该值就说嘛cookie添加成功了
                    HttpEntity entity = response.getEntity();
                    String responseContent = EntityUtils.toString(entity, "UTF-8");
                    System.out.println("=====================" + responseContent);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (null != response) {
                            response.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    try {
                        if (null != httpClient) {
                            httpClient.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //该方法就是为server参数表示的应用设置cookie也就是一个应用登录后,
        // 会调用setCookie方法为另一个应用设置cookie
        if (Objects.equals("/main", request.getServletPath())) {
            String domains = request.getParameter("domains");
            if (null != domains) {
                this.servers = domains;
            }
            String ticket = request.getParameter("ticket");
            if (null != domains && null != ticket) {
                for (String server : domains.split(",")) {
                    if (!Objects.equals(null, server) && !Objects.equals("", server.trim())) {
                        syncCookie(server, ticket, "setCookie");
                    }
                }
            }
            request.getRequestDispatcher("/WEB-INF/views/main.jsp").forward(request, response);
        } else if (Objects.equals("/setCookie", request.getServletPath())) {
            //也就是说,当一个应用登录成功后调用setCookie()方法,会触发一个请求的执行,就会执行另一个应用的这段else if 后的代码
            String ticket = request.getParameter("ticket");
            response.addCookie(new Cookie("Ticket_Granting_Ticket", ticket));
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/text; charset=utf-8");
            PrintWriter out = null;
            try {
                out = response.getWriter();
                out.write("ok");
                //ok的值会在另一个应用的setCookie方法中获得,得到ok代表cookie添加成功了
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != out) {
                    out.close();
                }
            }
        } else if (Objects.equals("/logout", request.getServletPath())) {
            Cookie cookie = new Cookie("Ticket_Granting_Ticket", null);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            if (null != servers) {
                for (String server : servers.split(",")) {
                    if (!Objects.equals(null, server) && !Objects.equals("", server.trim())) {
                        syncCookie(server, "", "removeCookie");
                    }
                }
            }
            request.getRequestDispatcher("/WEB-INF/views/logout.jsp").forward(request, response);
        } else if (Objects.equals("/removeCookie", request.getServletPath())) {
            Cookie cookie = new Cookie("Ticket_Granting_Ticket", null);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/text; charset=utf-8");
            PrintWriter out = null;
            try {
                out = response.getWriter();
                out.write("ok");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != out) {
                    out.close();
                }
            }
        }
    }

}

最终完成web.xml的全部配置



    
        UsreFilger
        com.jikewenku.sso.filter.UserFilter
        
            server
            http://127.0.0.1:8080
        
        
            app
            http://127.0.0.1:8081
        
    

    
        UsreFilger
        /*
    
    
        MainServlet
        com.jikewenku.sso.servlet.MainServlet
    
    
        MainServlet
        /main
    

    
        MainServlet
        /setCookie
    

    
        MainServlet
        /logout
    

    
        MainServlet
        /removeCookie
    

    
        MainServlet
        /ssoLogout
    

    
        default
        *.jpg
    
    
        default
        *.png
    
    
        default
        *.gif
    
    
        default
        *.ico
    
    
        default
        *.js
    
    
        default
        *.css
    


然后把WebApp1的代码拷贝到WebApp2中,注意拷贝web.xml的时候注意将端口改成8082.

然后拷贝我们准备好的main.jsp到views里面

效果演示

运行Server、Webapp1和WebApp2,首先在8081端口登录

然后新开一个标签进入8082端口,此时也登录成功了

源码下载

[fanctdl filename='SSO_Demo2.zip' filesize='5.66MB' href='https://pan.baidu.com/s/1rcWYxSASf6GQhuLYGn-uoA' filedown='百度网盘']回复可见密码[/fanctdl]

密码:[reply]gefd[/reply]

本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
极客文库 » JSP+Servlet+HttpClient实现单点登录教程(二)

3 评论

Leave a Reply

欢迎加入「极客文库」,成为原创作者从这里开始!

立即加入 了解更多