最新Java使用Servlet实现GitHub第三方登录
非原创 java_world 发表于:2018-08-25 20:26:25
  阅读 :110   收藏   编辑

1.创建GitHub账号,新增OAuth application

地址:https://github.com/settings/applications/new 

1

  • Application name 应用名称,跳转到Github将会看到

  • Homepage URL 网站主页 随便写

  • Application description 应用描述,随便写

  • Authorization callback URL 回调地址,在这里我们将获取用户的ID和用户名,调试时可以先行测试地址,到上线时更新为生产地址接口

创建好后,我们将获取到Client IDClient Secret

2

2.创建2个Servlet,一个用于登录跳转,一个用于回调

登录跳转Servlet :  ~ /plugin/GithubLogin

回调地址Servlet : ~/plugin/GithubLoginAfter

  • 定义一些常量

public class Static {
	
	public static final String client_id = "";//填写你的
	public static final String client_secret = "";//填写你的
	public static final String call_back = "";//填写你的
	public static final String auth_url = "https://github.com/login/oauth/authorize";
	public static final String token_url = "https://github.com/login/oauth/access_token";
	public static final String user_info_url = "https://api.github.com/user";
}
  • 本例中用的工具类

RandomKeyUtil.java

import java.util.Random;

public class RandomKeyUtil {
	
	/**
	 * @param length 随机数的长度,长度大于0,默认6位
	 * @param res    产生随机数的资源,可不传,默认为62个大小字符和10为阿拉伯数字
	 * @return
	 */
	public static String getRandomStr(int length,String ... res){
		if(length < 0 ) length = 6;
		if (res == null || res.length != 1) {
			res = new String[1];
			res[0] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
		}
		Random random = new Random();
		StringBuffer buf = new StringBuffer();
		for (int i = 0; i < length ; i++) {
			int num = random.nextInt(res[0].length());
			buf.append(res[0].charAt(num));
		}
		return buf.toString();
		
	}
	

}

EhCacheUtil.java

import org.springframework.cache.ehcache.EhCacheCacheManager;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
@Component
public class EhCacheUtil {

	private static EhCacheCacheManager cacheManager;
	
	@Autowired
	public  void setCacheManager(EhCacheCacheManager cacheManager) {
		EhCacheUtil.cacheManager = cacheManager;
	}


	/**
	 * get key
	 * @param cacheManager
	 * @param key
	 * @return
	 */
	public static Object getKeyByDefault(String key) {
		CacheManager cm = cacheManager.getCacheManager();
		Cache cache = cm.getCache("appDefault");
		net.sf.ehcache.Element getCache = cache.get(key);
		return getCache == null ? null : getCache.getObjectValue();
		
	}
	
	
	/**
	 * put key
	 * @param cacheManager
	 * @param key
	 * @return
	 */
	public static void putKeyByDefault(String key,Object obj) {
		CacheManager cm = cacheManager.getCacheManager();
		Cache cache = cm.getCache("appDefault");
		net.sf.ehcache.Element element = new net.sf.ehcache.Element(key, obj);
		cache.put(element);
	}
}

GithubLoginUser.java

public class GithubLoginUser implements Serializable{

	private String login;
	private String id;
	public String getLogin() {
		return login;
	}
	public void setLogin(String login) {
		this.login = login;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	
	
}

ehcache.xml 新增

<cache name="appDefault"
    maxEntriesLocalHeap="2000"
    eternal="false"
    overflowToDisk="true"
    timeToIdleSeconds="0"
     timeToLiveSeconds="3600"
    statistics="true">
</cache>

   http请求类

httpclient发送GET,POST请求,上传下载文件工具类

        redirect_uri:回调地址

        state : 随机写,主要用于攻击,在回调后用于校验,可存放在缓存或Session中

登录跳转Servlet 

/**
 * 
 * 跳转至github登录页面
 *
 */
public class GithubLogin extends HttpServlet {
	
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        	String url = "%s?client_id=%s&state=%s&redirect_uri=%s";
            try {
            	String state = RandomKeyUtil.getRandomStr(15);
            	Map<String, String> map  = (Map<String, String>) EhCacheUtil.getKeyByDefault("githubPluginState");
            	if(map == null) {
            		 map = new HashMap<String, String>();
            	}
            	map.put(state, "true");
            	EhCacheUtil.putKeyByDefault("githubPluginState", map);
				response.sendRedirect(String.format(url,Static.auth_url,Static.client_id,state,Static.call_back));
			} catch (Exception e) {
				e.printStackTrace();
			}
    }
    
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
          doGet(request,  response);
    }
    
}

登录成功后,在回调地址中可获取code,我们根据code再去获取token,根据token再去获取用户相关信息

登录回调Servlet 

public class GithubLoginAfter extends HttpServlet {
	
	private  Logger log = Logger.getLogger(GithubLoginAfter.class);
	
	
	/**
     * 获取token
     * @param code
     * @throws
     */
    public String getTokenByCode(String code) throws Exception {
        ReqParams params = ReqParams.init().addParam("code", code)
        				      .addParam("client_id", Static.client_id)
        				     .addParam("client_secret", Static.client_secret)
        				     .addParam("grant_type", "authorization_code")
        				      .addParam("redirect_uri", Static.call_back)
        				     ;
        
        HttpResponseUtils response = HttpRequestUtils.doGet(Static.token_url, params, "utf-8");
        if(response != null) {
        	 log.info(String.format("github第三方登录,获取accessToken返回:%s", response.getHtml()));
         	if(response.getStatusCode() == 200) {
         		String tokenHtml = response.getHtml();
         		 Matcher m = Pattern.compile("^access_token=(\\w+)&scope=(\\w+)&token_type=(\\w+)$").matcher(tokenHtml);
                 if (m.find()) {
                	 tokenHtml = m.group(1);
                 }else {
                	 Matcher m2 = Pattern.compile("^access_token=(\\w+)&scope=&token_type=(\\w+)$").matcher(tokenHtml);
                     if (m2.find()) {
                    	 tokenHtml = m2.group(1);
                     }
                 }
                 return tokenHtml;
        	}
        }
        return null;
    }
    
    /**
     *  获取用户信息
     * @param getUserInfo
     * @return
     */
    public GithubLoginUser getUserInfo(String accessToken) throws Exception{
        ReqParams params = ReqParams.init().addParam("access_token", accessToken);
        HttpResponseUtils response = HttpRequestUtils.doGet(Static.user_info_url, params, "utf-8");
        if(response != null) {
       	 log.info(String.format("github第三方登录,获取userInfo返回:%s", response.getHtml()));
       	if(response.getStatusCode() == 200) {
       		GithubLoginUser user = JSONObject.parseObject(response.getHtml(), GithubLoginUser.class);
       		return user;
       	}
       }
        return null;
    }
    
    
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         doPost(request, response);
    }
 
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
    	
    	 String path = request.getContextPath();
         String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
         
         String code = request.getParameter("code");
         String state = request.getParameter("state");
        
     	 Map<String, String> map  = (Map<String, String>) EhCacheUtil.getKeyByDefault("githubPluginState");
     	 if(map == null || map.get(state) == null) {
     		   log.info(String.format("github第三方登录,state校验失败:%s", state));
     		 response.getWriter().write("state valid error");
     		 return;
     	 }
     	
         if(ValidateUtil.validateBlank(code) || ValidateUtil.validateBlank(state)) {
        	 response.getWriter().write("state valid error");
     		 return;
         }
         log.info(String.format("github第三方登录,获取code:%s", code));
        try {
        	String accessToken = getTokenByCode(code);
        	if(ValidateUtil.validateBlank(accessToken)) {
        		log.info(String.format("github第三方登录,获取accessToken失败"));
        		response.getWriter().write("500 Error");
        		return;
        	}
        	log.info(String.format("github第三方登录,获取accessToken:%s",accessToken));
        	
        	
        	GithubLoginUser user  = getUserInfo(accessToken);
        	if(user == null) {
        		log.info(String.format("github第三方登录,获取userInfo失败"));
        		response.getWriter().write("500 Error");
        	}
        	String login = user.getLogin();
        	String id = user.getId();
        	log.info(String.format("github第三方登录,获取userInfo,login:%s",login));
        	log.info(String.format("github第三方登录,获取userInfo id:%s",id));
	              /**
                	 * 
                	 * 这里,就需要你处理自己的逻辑了,首先你已经获取了openid,这个是跟GitHub一一对应的,
                	 * 
                	 * 在你的数据库创建一张表,里面保存,网站登录帐号ID,openid ,
                	 * 
                	 * 用户在进行使用GitHub登录时,根据openid去表中获取帐号ID,
                	 * 如果不存在,跳转到绑定页面,进行保存数据库,然后调用登录逻辑,进行登录。
                	 * 如果存在,在数据查询该openid关联的帐号ID, 然后调用登录逻辑,进行登录。
                	 * 
                	 */
                	 
        } catch (Exception e) {
            e.printStackTrace();
        }
        response.getWriter().write("500 Error");
    }
}