1.创建 server app1 app2 3个项目 项目结构如下

配置 认证中心和子系统的域名 单点模拟需要 修改 host 配置文件
2,server 端 代码
创建登陆验证拦截器
package com.lz.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* @author pc-ZD
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在请求处理之前进行调用(Controller方法调用之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Map<String,Object> user = (Map<String,Object>) request.getSession().getAttribute("USER");
if (null == user){
//子系统 第一次登陆
response.sendRedirect("/login");
}else {
}
return true;
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
创建 控制器处理类
package com.lz.controller;
import com.alibaba.fastjson.JSONObject;
import com.lz.dao.RegisterApp;
import com.lz.dao.UserDB;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import static com.lz.dao.RegisterApp.appLogoutURL;
/**
* @author pc-ZD
*/
@Controller
public class LoginController {
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String toLogin(HttpServletRequest request,HttpServletResponse response){
try {
String redirectURL = request.getParameter("redirectURL");
request.getSession().setAttribute("redirectURL",redirectURL);
//如果 用户已经认证登陆 则将令牌 交给子系统,使其系统用户上线
Map<String,Object> user = (Map<String,Object>) request.getSession().getAttribute("USER");
if (null != user){
String token = user.get("token").toString();
if (null != redirectURL && !redirectURL.equals("")){
redirectURL += "?token=" + user.get("token");
response.sendRedirect(redirectURL);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return "login/login";
}
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(HttpServletRequest request, HttpServletResponse response,
@RequestParam Map<String, String> paramMap,@RequestParam String redirectURL){
try {
String userName = paramMap.get("userName") == null ? "" : paramMap.get("userName").trim();
String possWord = paramMap.get("possWord") == null ? "" : paramMap.get("possWord").trim();
if (userName.equals("admin") && possWord.equals("admin")){
Map<String,Object> user = new HashMap<>();
user.put("name","ADMIN");
user.put("userName","admin");
user.put("possWord","admin");
user.put("token", UUID.randomUUID().toString().trim());
if (null != redirectURL && !redirectURL.equals("")){
request.getSession().setAttribute("USER",user);
//登陆后 更新数据库 (保存 用户对应的密钥)
UserDB.currUserDB.add(user);
redirectURL += "?token=" + user.get("token");
response.sendRedirect(redirectURL);
}
return "login/fail2";
}
} catch (IOException e) {
e.printStackTrace();
}
return "login/fail";
}
/**
* 验证密钥
* @param token
* @param request
* @return
*/
@GetMapping("validToken/{token}")
public @ResponseBody Map<String,Object> validToken(@PathVariable("token") String token,HttpServletRequest request){
Map<String,Object> currUser = null;
for (int i = 0 ; i < UserDB.currUserDB.size() ;i++){
currUser = UserDB.currUserDB.get(i);
String userToken = currUser.get("token").toString();
if (userToken.equals(token)){
return currUser;
}
}
return null;
}
/**
* 通知子系统 注销 并注销当前 用户
*/
@RequestMapping(value = "/noticeLogout/{token}",method = RequestMethod.GET)
public String noticeLogout(HttpServletRequest request,@PathVariable("token") String token,
HttpServletResponse response,@RequestParam("redirectURL") String redirectURL){
try {
//通知子系统下线
for (String it: RegisterApp.appLogoutURL) {
CloseableHttpClient httpCilent = HttpClients.createDefault();
URI uri = new URI(it);
HttpGet httpget = new HttpGet(uri);
HttpResponse httpResponse = httpCilent.execute(httpget);
HttpEntity entity = httpResponse.getEntity();
String result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
((CloseableHttpResponse) httpResponse).close();
JSONObject resultMap = JSONObject.parseObject(result);
if (resultMap.get("code").equals(200) || resultMap.get("code").equals("200")){
System.out.println( "app :" + it +"通知成功");
}
}
//注销当前用户(认证中心 全局session)
request.getSession().invalidate();
//将当前令牌信息 清除
Map<String,Object> currUser = null;
for (int i = 0 ; i < UserDB.currUserDB.size() ;i++){
currUser = UserDB.currUserDB.get(i);
String userToken = currUser.get("token").toString();
if (userToken.equals(token)){
UserDB.currUserDB.remove(i);
}
}
//注销后回到原 app 首页
response.sendRedirect(redirectURL);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
return "login/logout";
}
@GetMapping("/registerAppLogout/{sessionId}")
public Map<String,Object> registerAppLogout(@RequestParam("appLogoutURL") String appLogoutURL,
@PathVariable("sessionId") String sessionId){
Map<String,Object> resultMap = new HashMap<>();
try {
RegisterApp.appLogoutURL.add(appLogoutURL + "/" + sessionId);
resultMap.put("code",200);
} catch (Exception e) {
resultMap.put("code",500);
e.printStackTrace();
}
return resultMap;
}
}
子系统应用注册 信息存储 ,用于通知对应的子系统注销
package com.lz.dao;
import java.util.ArrayList;
import java.util.List;
/**
* 这里使用 静态类模拟 redis 数据库
*/
public class RegisterApp {
/**
* key sessionId
* value logoutUrl
*/
public static List<String> appLogoutURL = new ArrayList<>();
}
创建认证中心 用户信息存储
package com.lz.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 这里使用 静态类模拟 redis 数据库
*/
public class UserDB {
public static List<Map<String,Object>> currUserDB = new ArrayList<>();
}
认证中心启动类配置
package com.lz;
import com.lz.interceptor.LoginInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author pc-ZD
*/
@ComponentScan("com.lz.*")
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
@Configuration
static class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry){
//指定拦截器类
registry.addInterceptor(new LoginInterceptor())
//指定该类拦截的url
.addPathPatterns("/*").excludePathPatterns("/login","/registerAppLogout/*");
}
}
}
认证登陆页
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
<form method="post" action="/login?redirectURL=${redirectURL!}">
<label>用户名</label>
<input type="text" name="userName" >
<label>密码</label>
<input type="text" name="possWord" >
<input type="submit">
</form>
</body>
</html>
应用子系统 1
同样创建登陆验证拦截器
package com.lz.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.lz.dao.SessionDB;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.util.Map;
/**
* @author pc-ZD
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在请求处理之前进行调用(Controller方法调用之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getParameter("token");
if (null != token && !token.equals("")){
// 去认证中心获取 验证 token 票 将 用户上线
CloseableHttpClient httpCilent = HttpClients.createDefault();
URI uri = new URI("http://sso.server.com:8888/validToken/"+ token);
HttpGet httpget = new HttpGet(uri);
HttpResponse httpResponse = httpCilent.execute(httpget);
HttpEntity entity = httpResponse.getEntity();
String result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
((CloseableHttpResponse) httpResponse).close();
// 令牌不存在
if (result.equals("")){
String redirectURL = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + request.getServletPath();
response.sendRedirect("http://sso.server.com:8888/login?redirectURL=" + redirectURL);
return false;
}
request.getSession().setAttribute("USER",JSONObject.parseObject(result));
//保存 客户端session 下线注销使用
SessionDB.DB.put(request.getSession().getId(),request.getSession());
//将当前 app 注销地址 注册到认证中心
String appLogoutURL = "http://sso.client1.com:8889/logout";
String sessionId = request.getSession().getId();
uri = new URI("http://sso.server.com:8888/registerAppLogout/" + sessionId + "?appLogoutURL=" + appLogoutURL);
httpget = new HttpGet(uri);
httpResponse = httpCilent.execute(httpget);
entity = httpResponse.getEntity();
result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
((CloseableHttpResponse) httpResponse).close();
if (result.equals(200) || result.equals("200")){
System.out.println(appLogoutURL + " 注册成功");
}else {
System.out.println(appLogoutURL + " 注册失败");
}
return true;
}
Map<String,Object> user = (Map<String,Object>) request.getSession().getAttribute("USER");
if (null == user){
String redirectURL = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + request.getServletPath();
response.sendRedirect("http://sso.server.com:8888/login?redirectURL=" + redirectURL);
return false;
}
return true;
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
创建控制处理类 子系统注销处理
package com.lz.controller;
import com.lz.dao.SessionDB;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @author pc-ZD
*/
@Controller
public class LoginController {
@RequestMapping(value = "/logout/{sessionId}",method = RequestMethod.GET)
public @ResponseBody
Map<String,Object> toLogin(HttpServletRequest request, @PathVariable("sessionId") String sessionId){
Map<String,Object> resultMap = new HashMap<>();
try {
SessionDB.DB.get(sessionId).invalidate();
resultMap.put("code",200);
} catch (Exception e) {
resultMap.put("code",500);
e.printStackTrace();
}
return resultMap;
}
}
创建子系统 用户信息存储 (也叫系统局部用户)
package com.lz.dao;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 这里使用 静态类 存储 客户端session
*/
public class SessionDB {
public static Map<String,HttpSession> DB = new HashMap<>();
}
子系统测试 请求接口
package com.lz.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author pc-ZD
*/
@Controller
public class IndexController {
@RequestMapping(value = "/",method = RequestMethod.GET)
public String toIndex(){
return "index";
}
@RequestMapping(value = "/userInfo",method = RequestMethod.GET)
public String userInfo(){
return "user/index";
}
}
测试启动类
package com.lz;
import com.lz.interceptor.LoginInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author pc-ZD
*/
@ComponentScan("com.lz.*")
@SpringBootApplication
public class Client1Application {
public static void main(String[] args) {
SpringApplication.run(Client1Application.class, args);
}
@Configuration
static class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry){
//指定拦截器类
registry.addInterceptor(new LoginInterceptor())
//指定该类拦截的url
.addPathPatterns("/*").excludePathPatterns("/","/logout");
}
}
}
创建子系统应用2 与 1基本相同
创建 登陆拦截器
package com.lz.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.lz.dao.SessionDB;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.util.Map;
/**
* @author pc-ZD
*/
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在请求处理之前进行调用(Controller方法调用之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getParameter("token");
if (null != token && !token.equals("")){
// 去认证中心获取 验证 token 票 将 用户上线
CloseableHttpClient httpCilent = HttpClients.createDefault();
URI uri = new URI("http://sso.server.com:8888/validToken/"+ token);
HttpGet httpget = new HttpGet(uri);
HttpResponse httpResponse = httpCilent.execute(httpget);
HttpEntity entity = httpResponse.getEntity();
String result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
((CloseableHttpResponse) httpResponse).close();
// 改令牌不存在
if (result.equals("")){
String redirectURL = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + request.getServletPath();
response.sendRedirect("http://sso.server.com:8888/login?redirectURL=" + redirectURL);
return false;
}
request.getSession().setAttribute("USER",JSONObject.parseObject(result));
//保存 客户端session 下线注销使用
SessionDB.DB.put(request.getSession().getId(),request.getSession());
//将当前 app 注销地址 注册到认证中心
String appLogoutURL = "http://sso.client2.com:8890/logout";
String sessionId = request.getSession().getId();
uri = new URI("http://sso.server.com:8888/registerAppLogout/" + sessionId + "?appLogoutURL=" + appLogoutURL);
httpget = new HttpGet(uri);
httpResponse = httpCilent.execute(httpget);
entity = httpResponse.getEntity();
result = EntityUtils.toString(entity, "utf-8");
EntityUtils.consume(entity);
((CloseableHttpResponse) httpResponse).close();
if (result.equals(200) || result.equals("200")){
System.out.println(appLogoutURL + " 注册成功");
}else {
System.out.println(appLogoutURL + " 注册失败");
}
return true;
}
Map<String,Object> user = (Map<String,Object>) request.getSession().getAttribute("USER");
if (null == user){
String redirectURL = "http://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + request.getServletPath();
response.sendRedirect("http://sso.server.com:8888/login?redirectURL=" + redirectURL);
return false;
}
return true;
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
创建控制器处理类 (用于用户注销)
package com.lz.controller;
import com.lz.dao.SessionDB;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author pc-ZD
*/
@Controller
public class LoginController {
@RequestMapping(value = "/logout/{sessionId}",method = RequestMethod.GET)
public @ResponseBody
Map<String,Object> toLogin(HttpServletRequest request, @PathVariable("sessionId") String sessionId){
Map<String,Object> resultMap = new HashMap<>();
try {
SessionDB.DB.get(sessionId).invalidate();
resultMap.put("code",200);
} catch (Exception e) {
resultMap.put("code",500);
e.printStackTrace();
}
return resultMap;
}
}
创建子系统的 用户 存储(局部用户)
package com.lz.dao;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
/**
* 这里使用 静态类 存储 客户端session
*/
public class SessionDB {
public static Map<String,HttpSession> DB = new HashMap<>();
}
测试请求接口
package com.lz.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author pc-ZD
*/
@Controller
public class IndexController {
@RequestMapping(value = "/",method = RequestMethod.GET)
public String toIndex(){
return "index";
}
@RequestMapping(value = "/userInfo",method = RequestMethod.GET)
public String userInfo(){
return "user/index";
}
}
启动类
package com.lz;
import com.lz.interceptor.LoginInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author pc-ZD
*/
@ComponentScan("com.lz.*")
@SpringBootApplication
public class Client2Application {
public static void main(String[] args) {
SpringApplication.run(Client2Application.class, args);
}
@Configuration
static class WebMvcConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry){
//指定拦截器类
registry.addInterceptor(new LoginInterceptor())
//指定该类拦截的url
.addPathPatterns("/*").excludePathPatterns("/","/logout");
}
}
}
测试结果
核心思想推荐阅读 :
https://blog.csdn.net/u011277123/article/details/53404269
我的小站 zegoto.cn 如有疑问 ,欢迎留言沟通
代码下载:
https://download.csdn.net/download/qq2531246791/10617326