• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

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

项目实战 Geekerstar 11个月前 (05-24) 512次浏览 已收录 2个评论 扫描二维码
文章目录[隐藏]

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进行配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.jikewenku.sso.servlet.LoginServlet</servlet-class>
        <init-param>
            <param-name>domains</param-name>
            <param-value>http://127.0.0.1:8081,http://127.0.0.1:8082</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/ssoLogin</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/ssoLogout</url-pattern>
    </servlet-mapping>
</web-app>

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

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>UsreFilger</filter-name>
        <filter-class>com.jikewenku.sso.filter.UserFilter</filter-class>
        <init-param>
            <param-name>server</param-name>
            <param-value>http://127.0.0.1:8080</param-value>
        </init-param>
        <init-param>
            <param-name>app</param-name>
            <param-value>http://127.0.0.1:8081</param-value>
        </init-param>
    </filter>
   <filter-mapping>
        <filter-name>UsreFilger</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

在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的全部配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <filter>
        <filter-name>UsreFilger</filter-name>
        <filter-class>com.jikewenku.sso.filter.UserFilter</filter-class>
        <init-param>
            <param-name>server</param-name>
            <param-value>http://127.0.0.1:8080</param-value>
        </init-param>
        <init-param>
            <param-name>app</param-name>
            <param-value>http://127.0.0.1:8081</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>UsreFilger</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <servlet>
        <servlet-name>MainServlet</servlet-name>
        <servlet-class>com.jikewenku.sso.servlet.MainServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MainServlet</servlet-name>
        <url-pattern>/main</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>MainServlet</servlet-name>
        <url-pattern>/setCookie</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>MainServlet</servlet-name>
        <url-pattern>/logout</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>MainServlet</servlet-name>
        <url-pattern>/removeCookie</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>MainServlet</servlet-name>
        <url-pattern>/ssoLogout</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.jpg</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.png</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.gif</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.ico</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.js</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.css</url-pattern>
    </servlet-mapping>

</web-app>

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

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

效果演示

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

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

源码下载

文件下载

密码:

注意:本段内容须成功“回复本文”后“刷新本页”方可查看!


丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:JSP+Servlet+HttpClient实现单点登录教程(二)
喜欢 (0)
[247507792@qq.com]
分享 (0)
Geekerstar
关于作者:
本站技术支持

您必须 登录 才能发表评论!

(2)个小伙伴在吐槽
  1. +1
    ehanla2018-10-03 09:56 Windows 10 | Chrome 67.0.3396.99
  2. 想要密码
    silentr2018-09-06 00:11 Windows 10 | Chrome 69.0.3497.81
  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00