单点登录的简单实现
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单点登录的简单实现相关的知识,希望对你有一定的参考价值。
实现的原理:
所有子系统的登录全部交由一个登录系统去实现,登录成功后生成一个ticket,并将ticket和用户名作为一个键值对保存在登录系统中,同时将用户名保存在cookie中。然后重定向回到原来的请求路径,子系统会根据ticket请求登录系统获取用户名,成功后,登录系统删掉ticket和用户名,并将用户名保存在session中,当session中有用户名后不会再被过滤重定向到登录系统。
当一个子系统登录成功后,请求其他子系统时,session中并没有用户名会重定向到登录系统,登录系统会获取cookie中的用户名,获取到用户名后会生成一个ticket,再次将ticket和用户名作为一个键值对保存在登录系统中,并再重定向会原来的请求并携带上ticket,此时就会根据ticket请求登录系统获取用户名,并存入session。
登出时只需要删除cookie,并在子系统做判断,当session中用户名与cookie中用户名不一致时,则认为没有登录,要求重新登录即可。
实现步骤:
因为需要同时启动3个项目(不同的端口号),一个登录系统,两个子系统,使用tomcat不太方便,这里使用的是jetty插件。
在pom.xml文件中进行配置,然后使用maven build启动即可。
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.9.v20130131</version>
<configuration>
<stopKey>stop</stopKey>
<stopPort>6001</stopPort> <!-- 3个项目的端口号都不相同 -->
<webAppConfig>
<contextPath>/client1</contextPath>
</webAppConfig>
<scanIntervalSeconds>4</scanIntervalSeconds>
<connectors>
<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
<port>8082</port> <!-- 3个项目的端口号都不相同 -->
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
</plugin>
</plugins>
登录系统:
登录效验:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
String service = req.getParameter("service");//原来请求的路径
/**
* 此处应该访问数据库效验用户
* */
if ("test".equals(username) && "test".equals(password)) {
Cookie cookie = new Cookie("sso", username);//效验通过,将用户名存入cookie
cookie.setPath("/"); //设置的cookie路径
resp.addCookie(cookie); //写入客户端
long time = System.currentTimeMillis();
String timeString = username + time; //生成ticket凭证
JVMCache.TICKET_AND_NAME.put(timeString, username); //存入缓存
/**
* 将ticket返回给原请求地址(登录效验)
* */
if (null != service) {
StringBuilder url = new StringBuilder();
url.append(service);
if (0 <= service.indexOf("?")) {
url.append("&");
} else {
url.append("?");
}
url.append("ticket=").append(timeString);
resp.sendRedirect(url.toString()); //请求原路径
} else {
resp.sendRedirect("/ssoServer/index.jsp"); //请求登录页面
}
} else {
resp.sendRedirect("/ssoServer/index.jsp?service=" + service);
}
}
过滤所有请求
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String service = request.getParameter("service"); //获取要访问的原地址
String ticket = request.getParameter("ticket"); //获取凭证
//获取cookie中的用户名
Cookie[] cookies = request.getCookies();
String username = "";
if (null != cookies) {
for (Cookie cookie : cookies) {
if ("sso".equals(cookie.getName())) {
username = cookie.getValue();
break;
}
}
}
//ticket不为空时,为子系统在效验是否登录
if (null == service && null != ticket) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
/**
* 用户已经登录
* */
if (null != username && !"".equals(username)) {
long time = System.currentTimeMillis();
String timeString = username + time; //生成ticket凭证
JVMCache.TICKET_AND_NAME.put(timeString, username); //存入缓存
StringBuilder url = new StringBuilder();
/**
* 重新生成token与用户名的缓存,并执行原请求(登录效验)
* */
url.append(service);
if (0 <= service.indexOf("?")) {
url.append("&");
} else {
url.append("?");
}
url.append("ticket=").append(timeString);
response.sendRedirect(url.toString()); //请求原路径并携带上ticket
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
返回用户名
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 通过效验ticket,返回给子系统用户名
* */
String ticket = req.getParameter("ticket");
String username = JVMCache.TICKET_AND_NAME.get(ticket);
JVMCache.TICKET_AND_NAME.remove(ticket);
//不为空时才返回
if(null!=username&&!"".equals(username)){
PrintWriter writer = resp.getWriter();
writer.write(username);
}
}
子系统:
/**
* 过滤所有请求,判断用户是否登录
* */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username"); //获取session里的用户名
String ticket = request.getParameter("ticket"); //获取ticket
String url = URLEncoder.encode(request.getRequestURL().toString(), "UTF-8");
//获取cookies里面的用户名
Cookie[] cookies = request.getCookies();
String tempname = "";
if (null != cookies) {
for (Cookie cookie : cookies) {
if ("sso".equals(cookie.getName())) {
tempname = cookie.getValue();
break;
}
}
}
//用户名为空说明未登录
if (null == username||!tempname.equals(username)) { //cookie和session用户名不一致说明已经登出
if (null != ticket && !"".equals(ticket)) {
//通过ticket向登录服务器发送请求获取对应用户名
PostMethod postMethod = new PostMethod("http://localhost:8081/ssoServer/ticket");
postMethod.addParameter("ticket", ticket);
HttpClient httpClient = new HttpClient();
try {
httpClient.executeMethod(postMethod);
username = postMethod.getResponseBodyAsString();
postMethod.releaseConnection();
} catch (Exception e) {
e.printStackTrace();
}
//用户名不为空说明已经登录,执行原请求
if (null != username && !"".equals(username)) {
session.setAttribute("username", username);
filterChain.doFilter(request, response);
} else { //用户名为空,未登录,跳转到登录页面
response.sendRedirect("http://localhost:8081/ssoServer/index.jsp?service=" + url);
}
} else { //用户名为空,未登录,跳转到登录页面
response.sendRedirect("http://localhost:8081/ssoServer/index.jsp?service=" + url);
}
} else { //已登录,执行原请求
filterChain.doFilter(request, response);
}
}
以上是关于单点登录的简单实现的主要内容,如果未能解决你的问题,请参考以下文章