架构图
sso认证中心
基于传统的servlet 方案
提供核心接口
1.登录接口
2.认证接口 判断是否已经登录 也可以进行手动的用户信息同步到各个子服务
3.登录页面
/**基于传统的 servlet 方案
*/
@WebServlet(urlPatterns = "/login.do")
public class LoginController extends HttpServlet {private static final String LOGIN_SESSION ="LOGIN_SESSION";private static final String LOGIN_TOKEN ="LOGIN_TOKEN";@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession session = req.getSession();String username = req.getParameter("username");String password = req.getParameter("password");
// 重定向的地址String url = req.getParameter("callback");if ("admin".equals(username) && "admin".equals(password)){
// 登录成功 存储用户信息到session中SessionMng mng = new SessionMng(username,password);session.setAttribute(LOGIN_SESSION,mng);//登录成功后 返回重定向的地址 if (url !=null && !url.equals("")){resp.sendRedirect(url);}else{req.getRequestDispatcher("/index.jsp").forward(req,resp);}}else{
// 登录失败 重定向到登录地址req.getRequestDispatcher("/login.jsp").forward(req,resp);}}
}
web.xml 配置 cookie 的名称 domain path 必须是父域名 让子域名共享
<?xml version="1.0" encoding="UTF-8"?>
<web-appversion="4.0"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:javaee="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xml="http://www.w3.org/XML/1998/namespace"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"><display-name>Archetype Created Web Application</display-name><session-config><cookie-config><name>session</name><domain>at.com</domain><path>/</path></cookie-config></session-config>
</web-app>
package com.me.web.controller;import com.alibaba.fastjson.JSONObject;
import com.me.domain.RedisCache;
import com.me.domain.SessionMng;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;/*** 验证登录请求是否合法*/
@WebServlet("/validlogin")
public class ValidLoginController extends HttpServlet {private static final String LOGIN_SESSION ="LOGIN_SESSION";private static final String LOGIN_TOKEN ="LOGIN_TOKEN";@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/*** 从请求中获取 cookie 判断 是否携带了 请求的cookie session*/Cookie[] cookies = req.getCookies();if (cookies!=null){for (int i = 0; i < cookies.length; i++) {System.out.println(cookies[i].getName()+":"+cookies[i].getValue());}}JSONObject obj = new JSONObject();
// 从本地的session中获取 用户信息 获取不到则视为未登录HttpSession session = req.getSession();SessionMng pbj = (SessionMng) session.getAttribute(LOGIN_SESSION);if (pbj!=null ){obj.put("success",true);}else{obj.put("success",false);}resp.getWriter().write(obj.toJSONString());}
}
登录页 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
String callback = request.getParameter("callback");
if (callback == null){callback ="";
}
%>
<html>
<head><title>login</title>
</head>
<body>
<form method="post" action="${pageContext.request.contextPath}/login.do"><input name="callback" type="hidden" value="<%=callback%>" />username: <input name="username" value=""> <br>password:<input name="password" value=""> <br><input type="submit" value="login"></form>
</body>
</html>
serviceA
基于过滤器实现请求拦截和判断
package com.me.web.filter;import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;import java.io.IOException;
import java.net.HttpCookie;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;public class SSOFilter implements Filter {private String VALID_LOGIN_URL ="";private String REDIRECT_LOGIN_URL ="";@Overridepublic void init(FilterConfig filterConfig) throws ServletException {VALID_LOGIN_URL = filterConfig.getInitParameter("VALID_LOGIN_URL");REDIRECT_LOGIN_URL = filterConfig.getInitParameter("REDIRECT_LOGIN_URL");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;/*从本地的cookie中获取认证中心的session 如果获取不到 则直接 退出登录 重定向到认证中心的登录地址*/Cookie[] cookies = req.getCookies();if (cookies==null || cookies.length==0){resp.sendRedirect(REDIRECT_LOGIN_URL+"?callback="+req.getRequestURL());return;}Optional<Cookie> op = Arrays.stream(cookies).filter(c -> c.getName().equals("session")).findFirst();if (op.isPresent()){/** 获取到cookie后 去认证中心校验是否登录 返回 true false 成功则通过* 否则 重定向到登录地址* */Cookie cookie = op.get();String token = cookie.getValue();boolean b = validLogin(token,req);if (b){chain.doFilter(request,response);}else{resp.sendRedirect(REDIRECT_LOGIN_URL+"?callback="+req.getRequestURL());}}else{resp.sendRedirect(REDIRECT_LOGIN_URL+"?callback="+req.getRequestURL());
// chain.doFilter(request,response);}}@Overridepublic void destroy() {}/*** 调用远程接口 校验是否登录 同步用户信息到本地* @param token* @param req* @return*/public boolean validLogin(String token,HttpServletRequest req){HttpSession session = req.getSession();String url= VALID_LOGIN_URL;Collection<HttpCookie> collect = new ArrayList();/*调用接口时 把cookie 带上 否则无法实现session获取用户信息和判断* */Arrays.stream(req.getCookies()).forEach(p->{HttpCookie c = new HttpCookie(p.getName(), p.getValue());collect.add(c);});String body= HttpRequest.get(url).cookie(collect).execute().body() ;JSONObject obj = JSON.parseObject(body);if (obj !=null){Boolean success = obj.getBoolean("success");if (success){return success;}else{return false;}}return false;}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-appversion="4.0"xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:javaee="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xml="http://www.w3.org/XML/1998/namespace"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"><display-name>Archetype Created Web Application</display-name><filter><filter-name>SSOFilter</filter-name><filter-class>com.me.web.filter.SSOFilter</filter-class><init-param><param-name>VALID_LOGIN_URL</param-name><param-value>http://at.com:9081/sso-server/validlogin</param-value></init-param><init-param><param-name>REDIRECT_LOGIN_URL</param-name><param-value>http://at.com:9081/sso-server/login.jsp</param-value></init-param></filter><filter-mapping><filter-name>SSOFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><session-config><cookie-config><domain>service.at.com</domain><path>/</path></cookie-config></session-config>
</web-app>
验证
认证中心登录地址:http://at.com:9081/sso-server/login.jsp
serviceA: http://service.at.com:8081/serviceA/index.jsp
在浏览器输入http://service.at.com:8081/serviceA/index.jsp
初次未登录会被重定向到http://at.com:9081/sso-server/login.jsp?callback=http://service.at.com:8081/serviceA/index.jsp
登录成功后 会被重定向到 http://service.at.com:8081/serviceA/index.jsp
验证结束