基于Oauth2.0实现SSO单点认证

什么是单点?

借用百度百科的话:

单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

单点登录图解:

什么是Oauth2.0?

OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

引用阮一峰老师的文章:OAuth 2.0 的四种方式 - 阮一峰的网络日志

正文:

前段时间在开发自己博客的时候,需要用到登录,当时的第一想法是做简单的密码验证就行了。不过为了熟悉一下SSO的流程,还是准备试用一下。

让我从SSO转变为Oauth的地方,是因为:SSO的限制,SSO需要在同一主域名下才能有效地写入cookie例如   www.baidu.com,如果试用SSO ,SSO的域名就需要是 ***.baidu.com。当然,不排除有其他方法可以实现cookie的写入。但正常的SSO流程就是需要在同一主域名下。

而Oauth2.0并没有限制,而且Oauth2.0更符合我对单点登录的认知。

开发流程:

1:授权系统开发

授权系统,这里其实就当做是一个最简单的登录验证器使用就行,有一个基础的增删改查就行

package com.hao.sso.vali.controller;

import java.util.concurrent.TimeoutException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.hao.bios.core.cache.ZboxCacheUtil;
import com.hao.bios.core.utils.StringUtil;
import com.hao.bios.core.utils.ZboxResult;
import com.hao.sso.vali.SsoConstants;
import com.hao.sso.vali.service.SsoLoginService;
import com.hao.sso.vali.vo.SsoLoginValiVO;
import com.hao.sso.vali.vo.ZboxSsoAuthPassVO;

import net.rubyeye.xmemcached.exception.MemcachedException;

@Controller
@RequestMapping("/sso/login")
public class LoginController {
	
	
	@Autowired
	private ZboxCacheUtil cache;
	@Autowired
	private SsoLoginService ssoLoginService;

	@RequestMapping("/isLogin")
	@ResponseBody
	public boolean isLogin(HttpServletRequest req, String accessToken) {
		
		if (StringUtil.isEmpty(accessToken)) {
			return false;
		}
		ZboxSsoAuthPassVO authVO = cache.get(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, ZboxSsoAuthPassVO.class);
		if (authVO == null) {
			return false;
		}
		long outTime = authVO.getExpires();
		long nowTime = System.currentTimeMillis();
		if (outTime!=0 && nowTime > outTime) {
			return false;
		}
		return true;
	}
	
	
	
	@RequestMapping("/login")
	@ResponseBody
	public String login(ServletRequest request, ServletResponse response, String userName, String pwd) throws TimeoutException, InterruptedException, MemcachedException {
		HttpServletRequest req = (HttpServletRequest) request;
		boolean isLogin = ssoLoginService.checkLogin(userName, pwd);
		if (isLogin) {
			SsoLoginValiVO valiVO = new SsoLoginValiVO();
			String ip = getIpAddress(req);
			valiVO.setIp(ip);
			valiVO.setIsLogin(true);
			valiVO.setLoginTime(StringUtil.getNowTimeString());
			valiVO.setOutTime(0);
			valiVO.setUserId(userName);
			String key = StringUtil.getUUID();
			cache.put(SsoConstants.LOGIN_VALI_CACHE_POOL, key, valiVO);
			String sign = StringUtil.getUUID();
			cache.put(SsoConstants.LOGIN_AUTH_CACHE_POOL, sign, key);
			return sign;
		}
		return "";
	}
	
	@RequestMapping("/getAuthPass")
	@ResponseBody
	public ZboxResult getAuthPass(ServletRequest request, ServletResponse response, String sign) throws TimeoutException, InterruptedException, MemcachedException {
		ZboxResult rul = new ZboxResult();
		String key = cache.get(SsoConstants.LOGIN_AUTH_CACHE_POOL, sign, String.class);
		if (StringUtil.isEmpty(key)) {
			rul.setSuccess(false);
			rul.setMessage("授权码无效");
			return rul;
		}
		SsoLoginValiVO vali = cache.get(SsoConstants.LOGIN_VALI_CACHE_POOL, key, SsoLoginValiVO.class);
		if (vali == null) {
			rul.setSuccess(false);
			rul.setMessage("未登录");
			return rul;
		}
		if (vali.getIsLogin() == false) {
			rul.setSuccess(false);
			rul.setMessage("登陆失效");
			return rul;
		}
		long outTime = vali.getOutTime();
		long nowTime = System.currentTimeMillis();
		if (outTime!=0 && nowTime > outTime) {
			rul.setSuccess(false);
			rul.setMessage("登陆超时");
			return rul;
		}
		
		String accessToken = StringUtil.getUUID();
		long expires = System.currentTimeMillis() + 60*60*1000L;
		String refreshToken = StringUtil.getUUID();
		//获取用户信息
		String info = "";
		ZboxSsoAuthPassVO authVO= new ZboxSsoAuthPassVO();
		authVO.setAccessToken(accessToken);
		authVO.setExpires(expires);
		authVO.setInfo(info);
		authVO.setRefreshToken(refreshToken);
		authVO.setScope("all");
		authVO.setTokenType("zbox_user_pass");
		authVO.setUid(vali.getUserId());
		cache.put(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, authVO);
		rul.setSuccess(true);
		rul.setData(authVO);
		return rul;
	}
	
	@RequestMapping("/loginOut")
	@ResponseBody
	public boolean loginOut(ServletRequest request, String accessToken) throws TimeoutException, InterruptedException, MemcachedException {
		ZboxSsoAuthPassVO authVO = cache.get(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, ZboxSsoAuthPassVO.class);
		if (authVO != null) {
			cache.put(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken, null);
			cache.delete(SsoConstants.LOGIN_AUTH_VALI_CACHE_POOL, accessToken);
		}else {
			return false;
		}
		return true;
	}
	
	public String getSsoCookie(HttpServletRequest req) {
		Cookie[] cookies = req.getCookies();
		if (cookies!=null) {
			for (Cookie cookie : cookies) {
				String name = cookie.getName();
				if (SsoConstants.SSO_LOGIN_VALI_COOKIE_KEY.equals(name)) {
					return cookie.getValue();
				}
			}
		}
		return "";
	}
	
	public String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("X-Forward-For");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("x-forwarded-for");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}
}

具体其实就是几个接口:登录、注销、验证登录、授权令牌

2:业务系统接入

直接上逻辑流程:

1:用户访问业务系统

2:业务系统先验证本地cookie是否有效,

        无效:直接返回前台未登录;

        有效:访问认证系统,检查登录情况,未登录则返回业务系统,业务系统返回前台未登录

3:业务检查到未登录,跳转到认证系统登录页面进行登录,登录成功之后认证系统返回一个授权码

4:业务系统携带授权码,在后端使用双方协议的id和秘钥(授权系统给业务系统的一个账户密码)携带着授权码,去认证系统获取令牌。

5:业务系统拿到令牌后使用令牌访问认证系统的api。

详情请查看阮一峰日志:GitHub OAuth 第三方登录示例教程

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值