接入 GitHub OAuth 第三方登录

接入 GitHub OAuth 第三方登录

在Github创建app应用

填写基本信息

创建成功

在这里可以上传logo,修改创建时填写的各种信息。

记录下:Client IDClient Secret

认证的步骤

  1. 用户在系统页面上通过点击带有Client ID参数的url,跳转到授权页面
  2. 如果用户同意授权,则会被重定向到 Authorization callback URL地址,同时携带code参数。
  3. 系统通过code,去请求accessToken,然后再次通过accessToken获取到Github用户的信息。
  4. 有了Github用户的信息,就可以完成与当前系统用户的绑定,从而完成登录。

创建一个SpringBoot工程

yml

核心就是配置好Github的Client IDClient Secret

oauth:
  github:
    app-id: ff52a5d0642bc35d25f7 
    app-secret: 9a1db9b66e0d80c46a1391d7b0887612f22b889c

主页Controller & View

如果未登录会被重定向到登录页面

@Controller
@RequestMapping(value = { "/", "/index" })
public class IndexController {

	@GetMapping
	public ModelAndView index(HttpServletRequest request) {
		
		HttpSession session = request.getSession(false);
		Object attr = null;
		if (session == null || (attr = session.getAttribute("user")) == null ) {
			// 未登录
			return new ModelAndView("redirect:/login");
		}
		
		ModelAndView modelAndView = new ModelAndView("index/index");
		modelAndView.addObject("user", attr);
		return modelAndView;
	}
}

登录Controller & View

用户在登录页面,可以点击超链接,通过github登录

@Controller
@RequestMapping("/login")
public class LoginController {
	
	@Value("${oauth.github.app-id}")
	private String appId;
	
	@GetMapping
	public ModelAndView login () {
		ModelAndView modelAndView = new ModelAndView("login/login");
		modelAndView.addObject("appId", this.appId);
		return modelAndView;
	}
}
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>OAuth-Login</title>
	</head>
	<body>
		<a href="https://github.com/login/oauth/authorize?client_id=${appId}&state=123456">点击我使用Github登录</a>
	</body>
	<script type="text/javascript">
	</script>
</html>

client_id 参数是必须的。 state 是一个校验参数,由系统生成。用户授权后,重定向回来,依然会带上这个参数。可以通过它来判断请求的合法性。

用户授权后的重定向处理

获取到用户信息后,写入session。重定向回主页。完成登录。

@Controller
@RequestMapping("/oauth")
public class OauthController {

	private static final Logger LOGGER = LoggerFactory.getLogger(OauthController.class);

	private static final String ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";

	private static final String USER_URL = "https://api.github.com/user";

	@Autowired
	private RestTemplate restTemplate;

	@Value("${oauth.github.app-id}")
	private String appId;

	@Value("${oauth.github.app-secret}")
	private String appSecret;

	/**
	 * Github回调
	 * @param request
	 * @param code			
	 * @param state
	 * @return
	 */
	@GetMapping("/github")
	public ModelAndView github(HttpServletRequest request, @RequestParam("code") String code,
								@RequestParam(value = "state", required = false) String state) {
		
		LOGGER.info("Github Oath:code={}, state={}", code, state);
		
		/**
		 * 使用code获取accessToken
		 */
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
		httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);

		MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
		requestBody.add("client_id", this.appId);
		requestBody.add("client_secret", this.appSecret);
		requestBody.add("code", code);

		HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(requestBody, httpHeaders);

		ResponseEntity<JSONObject> responseEntity = this.restTemplate.postForEntity(ACCESS_TOKEN_URL, httpEntity,
				JSONObject.class);

		JSONObject jsonObject = responseEntity.getBody();
		String accessToken = jsonObject.getString("access_token");
		String scope = jsonObject.getString("scope");
		String tokenType = jsonObject.getString("token_type");

		LOGGER.info("accessToken={}, scope={}, tokenType={}", accessToken, scope, tokenType);

		/**
		 * 通过accessToken获取用户信息
		 */
		httpHeaders = new HttpHeaders();
		httpHeaders.set(HttpHeaders.AUTHORIZATION, "token " + accessToken);
		httpHeaders.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);

		responseEntity = this.restTemplate.exchange(USER_URL, HttpMethod.GET, new HttpEntity<Void>(null, httpHeaders),
				JSONObject.class);
		jsonObject = responseEntity.getBody();

		LOGGER.info("user={}", jsonObject);

		/**
		 * TODO 从用户信息中获取到id,匹配本地数据库关联的系统用户。从而完成登录逻辑。
		 */
		HttpSession session = request.getSession();
		session.setAttribute("user", JSON.toJSONString(jsonObject, SerializerFeature.PrettyFormat));
		return new ModelAndView("redirect:/index");
	}
}

最终获取到了用户的信息

完整的工程代码

Github官方文档