1.创建GitHub账号,新增OAuth application
地址:https://github.com/settings/applications/new
Application name 应用名称,跳转到Github将会看到
Homepage URL 网站主页 随便写
Application description 应用描述,随便写
Authorization callback URL 回调地址,在这里我们将获取用户的ID和用户名,调试时可以先行测试地址,到上线时更新为生产地址接口
创建好后,我们将获取到Client ID和Client Secret
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请求,上传下载文件工具类
GitHub第三登录跳转
API:https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&state={state} 用于获取code
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); } }
GitHub第三登录回调
API:https://github.com/login/oauth/access_token 用于获取token
API:https://api.github.com/user 用于用户ID和用户名
登录成功后,在回调地址中可获取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"); } }