第三方登录
我们为什么要使用第三方登录?
因为这些三方平台有很大的用户群体;有这些大平台背景,用户的接收度和认可度较好;直接登录,避免用户注册和登录的繁琐过程。这样,可以比较好的推广自己的网站和粘主用户。
要使用三方登录,就必须了解三方登录协议-OAuth2.0,OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。
OAuth 2.0的运行流程如下图:
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
客户端授权 - 授权码模式
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI",同时附上一个授权 码。
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后 台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和 更新令牌(refresh token)。
微信三方登录
封装一个工具类
public class HttpClientUtils {
/**
* 发送get请求
* @param url 请求地址
* @return 返回内容 json
*/
public static String httpGet(String url){
// 1 创建发起请求客户端
try {
HttpClient client = new HttpClient();
// 2 创建要发起请求-tet
GetMethod getMethod = new GetMethod(url);
// 3 通过客户端传入请求就可以发起请求,获取响应对象
client.executeMethod(getMethod);
// 4 提取响应json字符串返回
String result = new String(getMethod.getResponseBodyAsString().getBytes("utf8"));
return result;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
常量封装
public class WxConstants {
public static final String APPID = "你在官网获取到的appid";
public static final String SECURITY = "4a5d5615f93f24bdba2ba8534642dbb6";
public static final String GET_OPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
public static final String GET_WXUSER_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
}
后台
跳转到微信登录的controller
@PostMapping("/weChat")
public AjaxResult weChat(@RequestBody Map<String, String> params) {
try {
return loginInfoService.loginWechat(params);
} catch (Exception e) {
e.printStackTrace();
return new AjaxResult(false,"登录失败!");
}
}
service
@Override
public AjaxResult loginWechat(Map<String, String> params) {
//获取参数
String code = params.get("code");
String binderUrl = params.get("binderUrl");
//发起获取accesstoken
//获取accesstoken请求url
String url = WechatConstants.GET_ACCESSTOKEN_URL
.replace("APPID",WechatConstants.APPID)
.replace("SECRET",WechatConstants.SECURITY)
.replace("CODE",code);
//2.2 发请求
String jsonStr = HttpClientUtils.httpGet(url);
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
String accessToken = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
//3 通过openid(微信号的唯一性标识)
User user = userMapper.loadByOpenId(openid);
if (user!=null){
//3.1 已经绑定,做免密登录
//4 存放redis并返回token,其实为了前台不额外发一个请求可以把用户和token一起返回,但是密码要置空
LoginInfo loginInfo = loginInfoMapper.loadById(user.getLogininfo_id());
String token = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
// 返回token,用户,所以用map返回
Map<String,Object> result = new HashMap<>();
result.put("token",token);
loginInfo.setPassword(""); //但是密码要置空
result.put("user",loginInfo);
return new AjaxResult().setResult(result);
}else{
binderUrl=binderUrl+"?accessToken="+accessToken+"&openid="+openid;
return new AjaxResult(false,"binder").setResult(binderUrl);
}
}
绑定controller
@PostMapping("/binder")
public AjaxResult binder(@RequestBody LoginDto loginDto) {
try {
return loginInfoService.binder(loginDto);
} catch (Exception e) {
e.printStackTrace();
return new AjaxResult(false,"绑定失败!");
}
}
绑定service
@Override
public AjaxResult binder(LoginDto loginDto) {
String accessToken = loginDto.getAccessToken();
String openid = loginDto.getOpenid();
//用户不存在,创建用户和wxuser,并完成绑定
String url = WechatConstants.GET_USER_URL
.replace("ACCESS_TOKEN",accessToken)
.replace("OPENID",openid);
String jsonStr = HttpClientUtils.httpGet(url);
User user = null;
LoginInfo loginInfo = loginInfoMapper.loadByUserName(loginDto);
//判断用户是否存在,用户不存在创建用户同时创建logininfo
if (loginInfo==null){
user = wechatUserJsonStr2User(jsonStr,loginDto);
loginInfo = userService.addOfReturnLoginInfo(user);
}
else {
//如果用户存在并且密码正确,获取用户
String md5Pwd = MD5Utils.encrypByMd5(loginDto.getPassword() + loginInfo.getSalt());
if (!loginInfo.getPassword().equals(md5Pwd))
return new AjaxResult(false,"用户名或密码不正确!");
user = userMapper.loadByUsername(loginDto.getUsername());
}
//创建微信用户,并且和用户进行绑定
WechatUser wechatUser = wechatUserJsonStr2WechatUser(jsonStr);
wechatUser.setUser_id(user.getId());
wechatUserMapper.save(wechatUser);
//免密登录
// 返回token,用户,所以用map返回
String token = UUID.randomUUID().toString();
Map<String,Object> result = new HashMap<>();
result.put("token",token);
loginInfo.setPassword(""); //但是密码要置空
result.put("user",loginInfo);
return new AjaxResult().setResult(result);
}
private WechatUser wechatUserJsonStr2WechatUser(String jsonStr) {
JSONObject jsonObject = JSONObject.parseObject(jsonStr);
WechatUser wechatUser = new WechatUser();
wechatUser.setOpenid(jsonObject.getString("openid"));
wechatUser.setNickname(jsonObject.getString("nickname"));
wechatUser.setSex(jsonObject.getInteger("sex"));
wechatUser.setAddress(null);
wechatUser.setHeadimgurl(jsonObject.getString("headimgurl"));
wechatUser.setUnionid(jsonObject.getString("unionid"));
return wechatUser;
}
private User wechatUserJsonStr2User(String jsonStr, LoginDto loginDto) {
User user = new User();
user.setUsername(loginDto.getUsername());
user.setPhone(loginDto.getUsername());
user.setEmail(loginDto.getUsername());
user.setPassword(loginDto.getPassword());
user.setState(1);
user.setCreatetime(new Date());
return user;
}
主要写后端代码,前端代码略
前端再写跳转到授权页面,回调页面,绑定页面