Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录

首先,在搞代码之前,我们要了解什么是JWT?

这个一开始我也不太明白,后来经过百度阅读看了很多文章才明白,其实JWT就像一个工具,可以对字符串进行签名(加密)和解签(解密)以及验证字符串。这里只是简单的对JWT主要功能描述一下就行,更具体的了解JWT需要自行百度。

第二:Token为什么要存放在Redis?存储格式又是怎么样的?

当用户登录成功时,将Token放在Redis,同时设置该Redis key的过期时间 = JWT Token过期时间,那么等这个存放某Token的key过期之后,Redis会自动删除这个Redis key(那我就不用刻意的维护Token)。

第三:SSO登陆逻辑(这是根据自己的实际项目绘制的逻辑图,若有问题勿喷。嘿嘿!)

 

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第1张图片

 

SSO注销登录逻辑:

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第2张图片

 

效果展示:

1.子系统A点击登录:

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第3张图片

SSO验证登录成功后,将token存到Redis,如图示例:

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第4张图片

然后SSO使用ajax(jsonp)发送命令给所有子系统,让他们添加token到本地Cookie中,然后重定向回子系统

如图:登陆成功

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第5张图片

查看Cookie有没有Token?

子系统A(10.1.8.12)

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第6张图片

子系统B(10.1.0.192);

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第7张图片

以上是在子系统A(10.1.8.12)上完成了登录成功,根据SSO的定义,那么,我访问子系统B(10.1.0.192)就应该是免登录的,接下来验证一下,访问子系统B(10.1.0.192)

Java_Springmvc+Hibernate5+JWT+Redis实现SSO跨域单点登录_第8张图片

可以看到,在子系统A上登录之后,在子系统B上就免登陆了,也就是成功实现了跨域SSO单点登陆

功能演示到此完毕。接下来看看我是如何实现SSO跨域单点登录的。

 

 

1.子系统访问受保护资源,子系统拦截器拦截请求,如下:


public class LoginFilter implements Filter {

	private final Gson gson = new Gson(); 
	String NBC_SSO_LOGIN_URL = PropertiesUtils.getProperty("NBC_SSO_SERVER_LOGIN_URL");
	String TokenLogin_HttpURL = PropertiesUtils.getProperty("NBC_SSO_SERVER_HTTPURL_FOR_TOKEN_CHECK");
	String NBC_USER_TOKEN = PropertiesUtils.getProperty("NBC_SSO_CLIENT_COOKIENAME_FOR_TOKEN");
	String NBC_SSO_CLIENT_COOKIE_TIMEOUT = PropertiesUtils.getProperty("NBC_SSO_CLIENT_COOKIE_TIMEOUT");
	
	 
	 
	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
			throws IOException, ServletException {

		System.out.println("============   doFilter LoginFilter   ============");
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) resp;   
	    HttpSession session = request.getSession();
	    ServletContext application = session.getServletContext();
	    boolean IsLogin = false;
	    Object UID = session.getAttribute("UID");
	    if(UID!=null && application.getAttribute(UID.toString())!=null && application.getAttribute(UID.toString()).equals(session.getId())) {
	    	IsLogin = true;
	    } 
	    if(UID!=null) {
	    	System.err.println("=============application uid:"+application.getAttribute(UID.toString()));
	    }
	    System.err.println("IsLogin:"+IsLogin);
	    //已登陆则放行
	    if(IsLogin) {
	    	chain.doFilter(request, response);
	    }else {
	    	//校验配置文件
		    boolean error = false;
			StringBuffer errorInfo = new StringBuffer("");;
			String errorURL =  request.getContextPath() +"/error/commonError"; 
		    String[] arr1 = new String[] {NBC_USER_TOKEN,NBC_SSO_LOGIN_URL,NBC_SSO_CLIENT_COOKIE_TIMEOUT};
		    String[] arr2 = new String[] {"NBC_SSO_CLIENT_COOKIENAME_FOR_TOKEN","NBC_SSO_SERVER_LOGIN_URL","NBC_SSO_CLIENT_COOKIE_TIMEOUT"};
		    for(int i=0;i0) {
						ssoLoginUrl = NBC_SSO_LOGIN_URL+"&returnRUL="+returnRUL;
					}else {
						ssoLoginUrl = NBC_SSO_LOGIN_URL+"?returnRUL="+returnRUL;
					} 
				}
				 
			}catch(Exception e) {
				e.printStackTrace();
			}
			
			System.err.println("登陆拦截");
			//若请求中有token参数,则校验token有效性
		    String token = req.getParameter("token");
		    if(token!=null && !token.equals("")) {
		    	SSOCheckResult checkResult = SsoCheck.checkToken(token, TokenLogin_HttpURL, request, response);
		    	System.err.println("tokenOK 0 ?"+checkResult.isSuccess());
		    	System.err.println(gson.toJson(checkResult));
		    	if(checkResult.isSuccess()) {
		    		session.setAttribute("IsLogin",true); 
		    		NBC_ID_UsersVO user = checkResult.getUser();
		    		if(user!=null) {
		    			System.err.println("校验Cookie token,token有效,返回用户信息userInfo:"+gson.toJson(user));
		    			String fuserId = String.valueOf(user.getFuserID());
		    			session.setAttribute("UID", fuserId);  
		    			session.setAttribute("UserName",user.getUserName_CHS() );  
		    			application.setAttribute(fuserId, session.getId());
		    		}
		    		chain.doFilter(request, response); 
		    	}else {
		    		CookieUtils.clearCookie(NBC_USER_TOKEN, request, response); 
		    		response.sendRedirect(ssoLoginUrl);
		    	}
		    }else { 
		    	SSOCheckResult checkResult = SsoCheck.checkToken(null, TokenLogin_HttpURL, request, response); 
		    	
		    	System.err.println("tokenOK 1 ?"+checkResult.isSuccess());
		    	System.err.println(gson.toJson(checkResult));
		    	
		    	
		    	if(checkResult.isSuccess()) { 
		    		token = checkResult.getToken();
		    		session.setAttribute("IsLogin",true); 
		    		NBC_ID_UsersVO user = checkResult.getUser();
		    		if(user!=null) {
		    			System.err.println("校验Cookie token,token有效,返回用户信息userInfo:"+gson.toJson(user));
		    			String fuserId = String.valueOf(user.getFuserID());
		    			session.setAttribute("UID", fuserId);  
		    			session.setAttribute("UserName",user.getUserName_CHS() );  
		    			application.setAttribute(fuserId, session.getId());
		    		}
//		    		boolean existCookie = CookieUtils.existCookie(NBC_USER_TOKEN,request);
//		    		if(!existCookie) {
//		    			CookieUtils.addCookie(NBC_USER_TOKEN, token, Integer.parseInt(NBC_SSO_CLIENT_COOKIE_TIMEOUT) , request, response);
//		    		} 
		    		chain.doFilter(request, response);
		    	}else {
		    		//clear cookie and redirect to login 
		    		response.sendRedirect(ssoLoginUrl);
		    	} 
		    }
	    }
	    
		 

	}

	@Override
	public void destroy() {
		System.out.println("============   Destroy Filter LoginFilter   ============");
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		System.out.println("============   Init Filter LoginFilter   ============");
	}
 
}

以上拦截器中涉及到使用HttpURLConnection访问SSO校验Token有效性。SsoCheck.java


public class SsoCheck {
	
	private static final Gson gson = new Gson();
	private static String NBC_USER_TOKEN = PropertiesUtils.getProperty("NBC_SSO_CLIENT_COOKIENAME_FOR_TOKEN");
	
	public static SSOCheckResult checkToken(String token, String httpURL,HttpServletRequest request, HttpServletResponse response) {
		System.err.println("=============== SSOCheckResult checkToken ===============");
		String clientIp = ComputerInfoUtil.getIP(request);  
		SSOCheckResult result = new SSOCheckResult(false,500,"未知错误",null,token);
		//如果不存在token参数,则在Cookie中查找
		if(token==null || token.equals("")) {
			//Cookie中是否有Token
	    	Cookie[] cookies = request.getCookies(); 
	    	if(cookies!=null && cookies.length>0) { 
	    		for(Cookie cookie : cookies) { 
		    		if(cookie.getName().equals(NBC_USER_TOKEN)) {
		    			token = cookie.getValue();
		    		} 
		    	}
	    	} 
		} 
    	if(token==null || token.equals("")) {
			return new SSOCheckResult(false,404,"参数错误[token]");
		}
    	result.setToken(token);
		String encoding = "UTF-8";
		String returnURL = request.getRequestURL().toString();
		try {
			//创建URL对象
			URL url = new URL(httpURL);
			//创建连接 
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			// true -- will setting parameters
			conn.setDoOutput(true);
			// true--will allow read in from
			conn.setDoInput(true);
			// will not use caches
			conn.setUseCaches(false);
			// setting serialized
			//conn.setRequestProperty("Content-type", "application/x-java-serialized-object");
			conn.setRequestProperty("Content-type", "application/json; charset=UTF-8");
			conn.setRequestProperty("Charsert", "UTF-8"); 
			conn.setRequestMethod("POST");// default is GET   
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("Charsert", "UTF-8"); 
			// 1 min
			conn.setConnectTimeout(60000);
			// 1 min
			conn.setReadTimeout(60000); 
			//send args
			conn.addRequestProperty("token",(token==null)?"":URLEncoder.encode( token, "UTF-8")); 
			conn.addRequestProperty("clientIp",(clientIp==null)?"":URLEncoder.encode( clientIp, "UTF-8")); 
			conn.addRequestProperty("returnURL",returnURL==null?"":URLEncoder.encode( returnURL, "UTF-8"));  
			// connect to server (tcp)
			conn.connect(); 
			//获得响应码
		    int code = conn.getResponseCode();//200代表Http OK 
		    String TokenOK = (conn.getHeaderField("TokenOK")==null)?"":conn.getHeaderField("TokenOK"); 
		    String userInfo = (conn.getHeaderField("userInfo")==null)?"":conn.getHeaderField("userInfo"); 

		    result.setErrorCode(code);
		    if(code==HttpURLConnection.HTTP_OK && TokenOK.equals("true")){ 
		    	userInfo = userInfo.equals("")?userInfo:URLDecoder.decode(userInfo, encoding);
		    	userInfo = userInfo.replace("\"", "").replace("\\", "\"");
		    	
		    	NBC_ID_UsersVO user = new NBC_ID_UsersVO();
		    	if(!userInfo.equals("")) { 
		    		user = gson.fromJson(userInfo, NBC_ID_UsersVO.class); 
		    	}
		    	result = new SSOCheckResult(true,0,"",user,token);
			} 
			//关闭连接 
			conn.disconnect();
		}catch(Exception e) {
			e.printStackTrace();
			result.setSuccess(false);
			result.setErrorCode(500);
			result.setErrorMsg("服务器异常:"+e.getMessage()); 
		}
		return result;
	}

}

好了,子系统的逻辑已经处理好,接下来就差SSO端来校验子系统发出的Http校验Token请求了。

那么接下来,需要创建SSO认证中心项目。

创建springboot项目,pom.xml主要引入JWT、Jedis、HttpClient等依赖,以下是我的pom.xml



	4.0.0
	
		org.springframework.boot
		spring-boot-starter-parent
		2.2.0.BUILD-SNAPSHOT
		 
	
	
	war
	com.nbc
	com.nbc
	0.0.1-SNAPSHOT
	NBC_SSO
	Spring Boot SSO Demo

	
		1.8
		UTF-8
        5.23.0-RC1
        1.9.4 
        2.8.2
        0.9.5.2
        2.7.5
        5.1.0.Final
        5.2.0.Final  
        5.2.0.Final
        5.1.0.Final  
        1.0.1.Final 
        4.5.2
        1.0.3
        2.9.0
        4.12  
        1.2
        3.4.0
        2.9.9
        2.4
        20190722
        3.12
        1.7
        2.0.12
        5.2.0.BUILD-SNAPSHOT
        4.0
        4.0.0
        1.0.5
	

	
	     
		
			junit
			junit
			${junit.version}
			test
		
		
		
		
			com.google.code.gson
			gson
			${gson.version}
		
		
		
			org.json
			json
			${org.json.version}
		
		
		
			com.fasterxml.jackson.core
			jackson-core
			${jackson.version}
		
		
			com.fasterxml.jackson.core
			jackson-databind
			${jackson.version}
		
		
		
		
            net.sf.cssbox
            pdf2dom
            ${pdf2dom.version} 
        
        
            org.apache.pdfbox
            pdfbox
            ${pdfbox.version}
        
        
            org.apache.pdfbox
            pdfbox-tools
            ${pdfbox.version}
        
        
		 
		
		    redis.clients
		    jedis
		    ${jedis.version}
		 
		 
		  
        
        
		
		
			net.sf.json-lib
			json-lib
			${net.sf.json.version}
			jdk15
		
		
		
		
			com.auth0
			java-jwt
			${jwt.version}
		
		
        
			org.apache.httpcomponents
			httpclient
			${httpclient.version}
		
		
		
			org.projectlombok
			lombok
			true
		
		
		
		
			com.microsoft.sqlserver
			sqljdbc4
			${sqlserver.version}
		
		
		
		
		
			com.mchange
			c3p0
			${c3p0.version}
		
		
		
		
			org.springframework
			spring-orm
			${spring.version}
		   
        
		 
		
		    org.hibernate
		    hibernate-core
		    ${hibernate.version}
		
		
		
		    org.hibernate
		    hibernate-entitymanager
		    ${hibernate.version}
		 
		
		    org.hibernate
		    hibernate-osgi
		    ${hibernate.version}
		
		
		    org.hibernate
		    hibernate-envers
		    ${hibernate.version}
		
		
		    org.hibernate
		    hibernate-c3p0
		    ${hibernate.version}
		
		
		    org.hibernate
		    hibernate-proxool
		    ${hibernate.version}
		
		
		    org.hibernate
		    hibernate-infinispan
		    ${hibernate.version}
		
		
		    org.hibernate
		    hibernate-ehcache
		    ${hibernate.version}
		 
		
		
		
		
			javax.servlet
			javax.servlet-api
			${servlet.api.version}
			provided
		 
		
			javax.servlet
			jstl
			${jstl.version}
		
		
            org.apache.tomcat.embed
            tomcat-embed-jasper
        
        
            org.apache.tomcat
            tomcat-jsp-api
        
        
        
        
		
			org.springframework.boot
			spring-boot-starter
		
	    
		   org.springframework.boot
		   spring-boot-starter-web
		
		
			org.springframework.boot
			spring-boot-starter-test
			test
			
				
					org.junit.vintage
					junit-vintage-engine
				
			
		
	

	
	    nbs-sso
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	

	
		
			spring-milestones
			Spring Milestones
			https://repo.spring.io/milestone
		
		
			spring-snapshots
			Spring Snapshots
			https://repo.spring.io/snapshot
			
				true
			
		
	
	
		
			spring-milestones
			Spring Milestones
			https://repo.spring.io/milestone
		
		
			spring-snapshots
			Spring Snapshots
			https://repo.spring.io/snapshot
			
				true
			
		
	


相关依赖自行拷贝到你的项目。

如何整合Springmvc和Hibernate这个我就不多说,自己搞(因为我的SSO已经在用了,只能展示部分核心代码)。当然,需要完整的SSO代码也可以联系我,但要有偿喔(Q:2225629171)

 

SSO登陆模块:

@RequestMapping(value="/login",method=RequestMethod.GET)
	public String to_login(Model model,HttpServletRequest req,HttpSession session) {  
		System.err.println("================ SSO Login =============="); 
		String returnURL = "";
		try {
			returnURL = req.getParameter("returnURL");
			if(returnURL!=null && !returnURL.equals("")) {
				returnURL = URLDecoder.decode(returnURL, "UTF-8");
			} 
		}catch(Exception e) {
			e.printStackTrace();
		}
		System.err.println("returnURL:"+returnURL);
		model.addAttribute("user",new z_CF_UsersVO());  
		model.addAttribute("returnURL",returnURL);  
		return "login";
	}
	
	@RequestMapping(value="/login",method=RequestMethod.POST,produces = "application/json; charset=utf-8")
	@ResponseBody
	public String do_login(Model model,HttpServletRequest req,HttpServletResponse resp,HttpSession session) 
					throws Exception {
		JSONObject json = new JSONObject();
		String sessionId = session.getId();
		String ipv4 = ComputerInfoUtil.getIP(req);
		String login_method = req.getParameter("login_method");
		String username = req.getParameter("username");
		String password = req.getParameter("password");
	
		
		//1.校验参数
		System.err.println("执行校验参数");
		String[] params = new String[] {login_method,username,password};
		boolean paramOK = loginService.checkParam(params);
		if(!paramOK) { 
			json.put("success", false);
			json.put("info", "参数错误:"+gson.toJson(params));
			return json.toString();
		}
		//2.执行登陆校验
		PublicModel result = loginService.login(sessionId,ipv4,login_method,username,password);
		if(!result.isSuccess()) {
			json.put("success", false);
			json.put("info", "执行登陆校验时发生异常:"+result.getErrorMsg());
			return json.toString();
		}
		LoginResult loginResult = result.getObj();
		if(loginResult==null) {
			json.put("success", false);
			json.put("info", "执行登陆校验时发生异常:loginResult=null");
			return json.toString();
		} 
		System.err.println("loginResult.getCode():"+loginResult.getCode());
		if(!String.valueOf(loginResult.getCode()).equals(String.valueOf(LoginCode.First_Login)) && !String.valueOf(loginResult.getCode()).equals(String.valueOf(LoginCode.Permit_Login))) {
			System.err.println("========= 返回错误信息 ==========");
			json.put("success", true);
			json.put("loginResult", gson.toJson(loginResult));
			return json.toString();
		}
		//登陆有效  
		//判断Redis服务是否已启动
		System.err.println("查询Redis服务是否已启动");
		boolean IsStarted = jedisClient.IsRedisStarted();
		if(!IsStarted) { 
			json.put("success", false);
			json.put("info", "Redis服务未启动,请及时联系管理员,谢谢您的配合!");
			return json.toString();
		} 
		System.err.println("Redis服务已启动");
		//获取新的用户登陆信息
		z_CF_UsersVO currentUser = new z_CF_UsersVO();//将部分信息封装返回
		currentUser.setFuserID(loginResult.getFuserId());
		currentUser.setUserName_EN(loginResult.getUserName_EN());
		currentUser.setUserName_CHS(loginResult.getUserName_CHS()); 
		Map claims = new HashMap();
		claims.put("fuserId", String.valueOf(loginResult.getFuserId()));
		claims.put("userInfo",gson.toJson(currentUser));
		String newToken =  JwtUtil.genToken(claims,Long.parseLong(NBC_SSO_SERVER_JWT_TOKEN_EXPIRE));
		currentUser.setToken(newToken);
		
		//注销该用户在其他地方的登录信息
		System.err.println("注销该用户旧的登录信息");
		String key = "";
		String old_userInfo = "";
		try {
			if(currentUser!=null) { 
				key = NBC_SSO_SERVER_JEDIS_KEY_PREFIX+String.valueOf(currentUser.getFuserID());
				if(key!=null && !key.equals("")) {
					old_userInfo = jedisClient.get(key); 
				}
				System.err.println("old_userInfo:"+old_userInfo);
				if(old_userInfo!=null && !old_userInfo.equals("")) {
					jedisClient.del(key);
				}
			}
		}catch(Exception e) {
			System.err.println("注销用户登陆信息时发生异常:"+e.getMessage());
			e.printStackTrace();
		}
		 
		//注册该用户最新的登陆信息  
        if (newToken != null && key!=null && !key.equals("")) {    
        	jedisClient.set(key , gson.toJson(currentUser));
        	jedisClient.expire(key,Integer.parseInt(NBC_SSO_SERVER_JEDIS_KEY_TIMEOUT));
        }  
        System.err.println("newToken:"+newToken);
		json.put("success", true);
		json.put("loginResult",  gson.toJson(loginResult)); 
		json.put("token", newToken);
		System.err.println("返回数据:"+json.toString());
		return json.toString(); 
 
	}

 

SSO校验中心:(SsoCheckController.java)


@Controller
@RequestMapping("/ssocheck")
public class SsoCheckController {
	
	
	private static final Gson gson = new Gson();
	private static String NBC_SSO_SERVER_REGEX_OF_IP = PropertiesUtils.getProperty("NBC_SSO_SERVER_REGEX_OF_IP");
	private static String NBC_SSO_SERVER_JEDIS_KEY_PREFIX = PropertiesUtils.getProperty("NBC_SSO_SERVER_JEDIS_KEY_PREFIX");
	 
	 
	@Autowired
	private JedisClient jedisClient; 
	@Autowired
	private LogService logService;
	
	 
	
	
	@RequestMapping(value = "/tokencheck")
	public void ssocheck(HttpServletRequest request, HttpServletResponse response,HttpSession session) { 
		System.err.println("=============== server tokencheck ===============");
		String sessionId = session.getId();
		String token = request.getHeader("token"); 
		System.err.println("tokencheck token:"+token);
		String clientIp = request.getHeader("clientIp"); 
		String returnURL = request.getHeader("returnURL");  
		String errorURL = request.getContextPath()+"/error/commonError";
		String loginURL = request.getContextPath()+"/login?returnURL="+returnURL;
		String sonSysIp = ComputerInfoUtil.getIP(request);
		JSONObject json = new JSONObject();
		List event = new ArrayList();
		event.add("客户端请求SSO认证中心校验Token");
		try {
			//returnURL = getDecoderString(returnURL,encoding);
			if(token==null || token.equals("")) {  
				event.add("警告:token参数为空,服务器已重定向页面至登陆页面:"+loginURL); 
				logService.ssocheckToLog(sessionId,clientIp, false, event); 
				response.sendRedirect(loginURL);
				return;
			} 
			Pattern pattern = Pattern.compile(NBC_SSO_SERVER_REGEX_OF_IP);
			if(clientIp==null || clientIp.equals("")) {
				event.add("警告:clientIp参数为空,服务器已重定向页面至登陆页面:"+loginURL); 
				logService.ssocheckToLog(sessionId,clientIp, false, event); 
				response.sendRedirect(loginURL);
				return;
			}else if(!pattern.matcher(clientIp).matches()){
				event.add("警告:clientIp参数非法,该参数源自子系统ipv4:"+sonSysIp);
				event.add("服务器已重定向页面至登陆页面:"+loginURL); 
				logService.ssocheckToLog(sessionId,clientIp, false, event); 
				response.sendRedirect(loginURL);
				return;
			} 
			String encoding = "UTF-8";
			token = getDecoderString(token,encoding); 
			event.add("请求参数Token:"+token);
			
			boolean tokenOK = JwtUtil.checkToken(token);
			System.err.println("校验Token:"+((tokenOK==true)?"有效":"无效"));
			if(!tokenOK) {
				event.add("警告:Token认证不通过,该客户端存在伪造Token的可能性,请及时做好应对措施。");
				event.add("服务器已重定向页面至登陆页面:"+loginURL); 
				logService.ssocheckToLog(sessionId,clientIp, false, event);
				response.sendRedirect(loginURL);
				return;
			}
			
			String redis_userInfo = "";
			String fuserId = JwtUtil.getClaim("fuserId", token);
			if(fuserId!=null) {
				String key = NBC_SSO_SERVER_JEDIS_KEY_PREFIX+AesUtil.decode(fuserId);
				System.err.println("key:"+key);
				redis_userInfo = jedisClient.get(key); 
			} 
			System.err.println("redis_userInfo:"+redis_userInfo);
			if(redis_userInfo==null || redis_userInfo.equals("")) {
				//token不存在于服务器redis
				event.add("警告:Token不存在于服务器,服务器已重定向页面至登陆页面:"+loginURL); 
				logService.ssocheckToLog(sessionId,clientIp, false, event); 
				response.sendRedirect(loginURL);
				return;
			} 
			//token有效并且服务器有该用户登陆信息
			//校验token和登陆信息的token是否一致
			z_CF_UsersVO user = gson.fromJson(redis_userInfo, z_CF_UsersVO.class);
			if(!token.equals(user.getToken())) {
				event.add("token有效并且服务器有该用户登陆信息,但token和服务器的token不一致(可能是该用户已在其他地方登陆)"); 
				logService.ssocheckToLog(sessionId,clientIp, false, event); 
				response.sendRedirect(loginURL);
				return;
			}
			String TokenOK1 = "true";
			String userInfo = AesUtil.decode(JwtUtil.getClaim("userInfo", token)); 
			String userInfo1 = (userInfo==null)?"":URLEncoder.encode(gson.toJson(userInfo), encoding);
			json.put("TokenOK", TokenOK1);
			json.put("userInfo", userInfo);
			event.add("Token认证通过,即将返回信息给客户端:"+json.toString()); 
			logService.ssocheckToLog(sessionId,clientIp, true, event);
			response.setHeader("TokenOK", TokenOK1);
			response.setHeader("userInfo",userInfo1);	
		}catch(Exception e) {
			try {
				String info = "SSO认证中心校验Token时发生异常:"+e.getMessage(); 
				//checktoken记录日志
				event = new ArrayList();
				event.add(info);
				event.add("服务器已重定向页面至错误页面:"+errorURL);
				logService.ssocheckToLog(sessionId,clientIp, false, event);
				request.getSession().setAttribute("error", info);
				response.sendRedirect(errorURL);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
		 
		 
	}
	 
	private String getEncoderString(String str,String encoding) {
		String result = "";
		try {
			if(str!=null && !str.equals("") && encoding!=null && !encoding.equals("")) {
				result = URLEncoder.encode( str, encoding); 
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	private String getDecoderString(String str,String encoding) {
		String result = "";
		try {
			if(str!=null && !str.equals("") && encoding!=null && !encoding.equals("")) {
				result = URLDecoder.decode( str, encoding); 
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
		return result;
	}

}

JWT工具类:


@Slf4j
public class JwtUtil {
	
 
	
//	public static void main(String[] args) {
//		long expire = 1000 * 60;
//		Map map = new HashMap();
//		map.put("fuserId", "2");
//		map.put("username", "admin");
//		String token = genToken(map,expire);
//		System.err.println(token);
//		Map claims = getClaims(token);
//		for(String key : claims.keySet()) {
//			//System.err.println(key);
//			//System.err.println(key+":"+AesUtil.decode(claims.get(key)));
//		}
//		//String claim = getClaim("fuserId",token);
//		//System.err.println(AesUtil.decode(claim));
//		
//	}

	private static final Gson gson = new Gson();
	
    //密钥
    private static String SECRET = readSecret();;
    //发行人
    private static String ISSUER = "NBC_USER";
 
    public static String getClaim(String claimName, String token) { 
    	String[] params = new String[] {claimName,token};
    	for(String param : params) {
    		if(param==null || param.equals("")) {
    			return null;
    		}
    	}
    	try {
    		Algorithm algorithm = Algorithm.HMAC256(SECRET);
    		//解密
            JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).build();
            DecodedJWT jwt =  verifier.verify(token); 
            Map map = jwt.getClaims();
            Map resultMap = new HashMap<>();
            map.forEach((k,v) -> resultMap.put(k, v.asString())); 
            return resultMap.get(claimName);
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return null;
    }
    public static Map getClaims(String token) { 
    	
    	if(token==null || token.equals("")) {
    		return null;
    	}
    	try {
    		Algorithm algorithm = Algorithm.HMAC256(SECRET);
    		//解密
            JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).build();
            DecodedJWT jwt =  verifier.verify(token); 
            Map map = jwt.getClaims();
            Map resultMap = new HashMap<>();
            map.forEach((k,v) -> resultMap.put(k,v.asString()));
            return resultMap; 
    	}catch(Exception e) {
    		e.printStackTrace();
    	}
    	return null;
    }
    /**
     * 生成jwt
     */
    public static String genToken(Map claims,long expire) {
    	try { 
    		//过期时间
    		Date expireDate = new Date(System.currentTimeMillis() + expire);
        	//使用HMAC256进行加密
            Algorithm algorithm = Algorithm.HMAC256(SECRET); 
            //创建jwt
            JWTCreator.Builder builder =  JWT.create()
            		.withIssuer(ISSUER)//发行人
            		.withExpiresAt(expireDate); //过期时间点
            //传入参数
            if(claims!=null) {
            	claims.forEach((key,value)-> {
            		String encodeValue = AesUtil.encode(value);
                    builder.withClaim(key, encodeValue);
                });
            } 
            //签名加密
            return builder.sign(algorithm);
        } catch (Exception e) {
            //log.info("jwt 生成问题");
        } 
        return null;
    }

    /**
     * 解密jwt并验证是否正确
     */
    public static boolean checkToken (String token) {
    	if(token==null || token.equals("")) {
    		return false;
    	} 
        try {
            //使用hmac256加密算法
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
                    .withIssuer(ISSUER)
                    .build();
            DecodedJWT decodedJWT = verifier.verify(token);
            //获得token的头部,载荷和签名,只对比头部和载荷
            String [] headPayload = token.split("\\.");
            //获得jwt解密后头部
            String header = decodedJWT.getHeader();
            //获得jwt解密后载荷
            String payload = decodedJWT.getPayload(); 
            return (header.equals(headPayload[0]) && payload.equals(headPayload[1]));
        } catch (Exception e) {
            //log.info("jwt解密出现错误,jwt或私钥或签证人不正确");
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 读取密钥
     * @return 密钥信息
     */
    private static String readSecret() {
    	String secret = "";
    	try {
    		Properties properties = new Properties(); 
            InputStream inputStream = ClassLoader.getSystemResourceAsStream("jwt.properties");//加载resource目录下的配置文件
            properties.load(inputStream); 
            secret = properties.getProperty("JWT_SECRET");
    	}catch(Exception e) {
    		e.printStackTrace();
    	} 
        return secret;
    }
}

AesUtil.java:


@Slf4j
public class AesUtil {

	  
    //加密密钥
    private static String SECRET = readSecret();;


	/**
     * 加密
     * @return 加密后内容
     */
    public static String encode (String content) {
        Key key = getKey();
        byte[] result = null;
        try{
            //创建密码器
            Cipher cipher = Cipher.getInstance("AES");
            //初始化为加密模式
            cipher.init(Cipher.ENCRYPT_MODE,key);
            //加密
            result = cipher.doFinal(content.getBytes("UTF-8"));
           //将二进制转换成16进制
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < result.length; i++) {
                String hex = Integer.toHexString(result[i] & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                sb.append(hex.toUpperCase());
            }
            return  sb.toString();
        } catch (Exception e) {
            //log.info("aes加密出错");
        }
        return null;
    }

    /**
     * 解密
     * @return 解密后内容
     */
    public static String decode (String content) {
        //将16进制转为二进制
        if (content.length() < 1)
            return null;
        byte[] result = new byte[content.length()/2];
        for (int i = 0;i< content.length()/2; i++) {
            int high = Integer.parseInt(content.substring(i*2, i*2+1), 16);
            int low = Integer.parseInt(content.substring(i*2+1, i*2+2), 16);
            result[i] = (byte) (high * 16 + low);
        }

        Key key = getKey();
        byte[] decrypt = null;
        try{
            //创建密码器
            Cipher cipher = Cipher.getInstance("AES");
            //初始化为解密模式
            cipher.init(Cipher.DECRYPT_MODE,key);
            //解密
            decrypt = cipher.doFinal(result);
        } catch (Exception e) {
            //log.info("aes解密出错");
        }
        assert decrypt != null;
        return new String(decrypt);
    }

    /**
     * 根据私钥内容获得私钥
     */
    private static Key getKey () {
        SecretKey key = null;
        try {
            //创建密钥生成器
            KeyGenerator generator = KeyGenerator.getInstance("AES");
            //初始化密钥
            generator.init(128,new SecureRandom(SECRET.getBytes()));
            //生成密钥
            key = generator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return key;
    }

    /**
     * 读取密钥
     * @return 密钥信息
     */
    public static String readSecret () {
    	String secret = ""; 
        try {
        	Properties properties = new Properties();
            //加载resource目录下的配置文件
            InputStream inputStream = ClassLoader.getSystemResourceAsStream("aes.properties"); 
            properties.load(inputStream);
            secret = properties.getProperty("AES_SECRET");
        } catch (IOException e) {
            //log.info("读取密钥文件错误");
        }
        return secret;
    }

}

SSO登录成功后,跳转至SSO登陆成功处理页面loginSuccess.jsp(ajax发送命令,让子系统添加token到Cookie后,重定向页面回子系统指定的LoginOK页面)

@RequestMapping(value="/loginSuccess",method=RequestMethod.GET)
	public String loginSuccess(Model model,HttpServletRequest req,HttpSession session) {
		 
		String domains = "";
		String returnURL = (req.getParameter("returnURL")==null)?"":req.getParameter("returnURL");
		String token = (req.getParameter("token")==null)?"":req.getParameter("token");
		try { 
			List list = new ArrayList();
			List list0 = domainRepository.findAll();
			list0.forEach((domain)->{
				z_CF_DomainVO vo = new z_CF_DomainVO();
				BeanUtils.copyProperties(domain, vo);
				list.add(vo);
			});
			domains = ScriptDecoder.escape(gson.toJson(list));
		}catch(Exception e) {
			e.printStackTrace();
		}
		session.setAttribute("domains",domains ); 
		session.setAttribute("returnURL", returnURL); 
		session.setAttribute("token", token);
		return "public/loginSuccess";
	}
	
	
	@RequestMapping(value="/return",method=RequestMethod.POST)
	public String returnPage(Model model,HttpServletRequest req,HttpServletResponse resp,HttpSession session) {
		String returnURL = (req.getParameter("returnURL")==null)?"":req.getParameter("returnURL");
		String token = (req.getParameter("token")==null)?"":req.getParameter("token");
		String type = (req.getParameter("type")==null)?"":req.getParameter("type");
		session.setAttribute("type", type); 
		session.setAttribute("returnURL", returnURL); 
		session.setAttribute("token", token); 
		return "public/return";
	}

loginSuccess.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>




loginSuccess
<%
System.err.println("=============== SSO loginSuccess.jsp ===============");
%>



 





return.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
System.err.println("================= return.jsp =================");
String returnURL = (session.getAttribute("returnURL")==null)?"":session.getAttribute("returnURL").toString();
String token = (session.getAttribute("token")==null)?"":session.getAttribute("token").toString();
String type = (session.getAttribute("type")==null)?"":session.getAttribute("type").toString();
if(!returnURL.equals("")){  
	String returnType = "";
	switch(type.toLowerCase()){
		case "login":
			returnType = "登陆";
			if(!token.equals("")){
				if(returnURL.indexOf("?")>0){
					returnURL = returnURL+"&token="+token;
				}else{
					returnURL = returnURL+"?token="+token;
				}
			}  
			break;
		case "logout":
			returnType = "注销"; 
			break;
		default:  
			break;
	} 
	System.err.println("类型:"+returnType);
	System.err.println("服务器即将重定向回子系统:"+returnURL);
	response.sendRedirect(returnURL);
}

%>

好了,SSO大部分核心代码已经贴出来了,接下来就看你们的编码功底了。有什么相关问题也可以找我讨论(q:2225629171)

本次SSO实现跨域单点登陆讲解完毕。

你可能感兴趣的:(java)