接入 GitHub OAuth 第三方登录
在Github创建app应用
填写基本信息
创建成功
在这里可以上传logo,修改创建时填写的各种信息。
记录下:Client ID
和 Client Secret
认证的步骤
- 用户在系统页面上通过点击带有
Client ID
参数的url
,跳转到授权页面 - 如果用户同意授权,则会被重定向到
Authorization callback URL
地址,同时携带code参数。 - 系统通过
code
,去请求accessToken
,然后再次通过accessToken
获取到Github用户的信息。 - 有了Github用户的信息,就可以完成与当前系统用户的绑定,从而完成登录。
创建一个SpringBoot工程
yml
核心就是配置好Github的Client ID
和 Client 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");
}
}