社交登录,是指用户可以使用微博、QQ、微信等社交媒体账号登录网站。从用户的角度来看,社交登录不仅可以省去注册账号的操作过程,也可以不用记忆账号和密码,网站接入社交登录功能是真的香。该如何接入社交登录呢?接入流程是怎样的呢?别急,且听我慢慢道来。
一、环境搭建
1. 申请微博登录接口
以微博登录为例,你首先需要到微博开放平台申请一个网页应,填写应哟用相关信息。
等待审核通过,就可以获得一个App Key和App Secret,你需要设置授权回调页的地址,现在你就可以使用微博登入接口了。
2 社交登录流程
在微博开放平台有关于OAuth2.0协议的授权流程图。
我来带大家解读一下这个授权流程。
- 首先点击微博登录按钮,需要携带client_id和回调地址(必须和微博开放平台上设置的授权回调页地址相同)到如下地址 :
https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
- 用户使用微博账号和密码进行授权,授权成功,重定向到:
YOUR_REGISTERED_REDIRECT_URI/?code=CODE
-
接下来带着code码和回调地址,发送POST请求到微博的认证服务器换取access_token。
https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE
必须携带以下请求参数。
client_id:申请应用时分配的AppKey
client_secret:申请应用时分配的AppSecret
grant_type:请求的类型,authorization_code
redirect_url:回调地址,必须与注册应用里的回调地址一致
code:上一步骤返回的code码
返回数据:
-
认证服务器验证通过,返回access_token,然后就可以拿着这个access_token去访问微博的API服务器,发送GET请求获取用户的开放信息。比如:根据用户id获取用户信息。
https://api.weibo.com/2/users/show.json?access_token=ACCESS_TOKKEN&uid=用户ID
返回数据
3 数据库设计
创建一张用户表,保存用户基本信息,并添加社交id和社交令牌字段,social_uid作为社交用户的唯一身份识别。
create table ums_member
(
id bigint not null auto_increment comment 'id',
username char(64) comment '用户名',
password varchar(64) comment '密码',
nickname varchar(64) comment '昵称',
profile_image_url varchar(255) comment '头像地址',
gender tinyint comment '性别',
create_time datetime comment '注册时间',
social_uid varchar(255) comment '社交id',
access_token VARCHAR(255) COMMENT '社交令牌',
primary key (id)
);
二、实现流程
1. 登录页面
微博登录按钮添加链接,引导用户到授权页面
<div class="si_out">
<a href="https://api.weibo.com/oauth2/authorize?client_id=APP_KEY&response_type=code&redirect_uri=http://auth.ylogin.com/oauth2.0/weibo/success">
<img th:width="50px" th:height="18px" src="/static/login/JD_img/weibo.png" />
</a>
</div>
2. 处理登录请求
- 首先获取到请求参数code的值,发送POST请求换取access_token,调用远程用户服务登录
@Slf4j
@Controller
public class OAuth2Controller {
@Autowired
UserFeignService userFeignService;
@GetMapping("/oauth2.0/weibo/success")
public String weibo(@RequestParam("code") String code, HttpSession session, HttpServletResponse servletResponse) throws Exception {
// 通过Authorization Code获取Access Token
Map<String, String> map = new HashMap<>();
map.put("grant_type","authorization_code");
map.put("client_id",""); //app id
map.put("client_secret",""); //app key
map.put("code",code);
map.put("redirect_uri","http://auth.ylogin.com/oauth2.0/weibo/success");
HttpResponse res = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post",new HashMap<String, String>(), map, (byte[]) null);
// 成功换取Access Token
if (res.getStatusLine().getStatusCode() == 200){
// 获取返回数据
String json = EntityUtils.toString(res.getEntity());
SocialUserTo socialUserTo = JSON.parseObject(json, SocialUserTo.class);
// 调用远程用户服务进行登录
R r = userFeignService.socialLogin(socialUserTo);
if (r.getCode() == 0) {
UserResponseVo data = r.getData("data", new TypeReference<UserResponseVo>() {
});
log.info("登录成功!用户信息:{}",data.toString());
session.setAttribute(AuthServerConstant.LOGIN_USER,data);
return "redirect:http://ylogin.com";
} else {
return "redirect:http://auth.ylogin.com/login.html";
}
} else {
return "redirect:http://auth.ylogin.com/login.html";
}
}
}
- 用户接收换取Access Token的请求返回数据的实体类
@Data
public class SocialUserTo {
private String accessToken;
private long expiresIn;
private String refreshToken;
private String uid;
private String remindIn;
}
3. 用户登录
- 如果用户已经注册,直接登录;如果用户未进行注册,则拿access_token和用户id获取用户的信息,进行注册。
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {
@Override
public UserEntity login(SocialUserTo socialUserTo) {
// 登陆和注册合并逻辑
String uid = socialUserTo.getUid();
UserDao userDao = this.baseMapper;
UserEntity member = userDao.selectOne(new QueryWrapper<UserEntity>().eq("social_uid", uid));
if (member != null){
// 当前社交用户已经注册,更新用户的access_token
UserEntity updateMember = new UserEntity();
updateMember.setId(member.getId());
updateMember.setAccessToken(socialUserTo.getAccessToken());
userDao.updateById(updateMember);
member.setAccessToken(socialUserTo.getAccessToken());
return member;
} else {
// 没有查到当前社交用户,需要注册
UserEntity register = new UserEntity();
try {
// 获取当前社交用户的信息
Map<String, String> query= new HashMap<>();
query.put("access_token",socialUserTo.getAccessToken());
query.put("uid",socialUserTo.getUid());
HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "GET", new HashMap<String, String>(), query);
if (response.getStatusLine().getStatusCode() == 200) {
// 查询成功
String json = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(json);
// 获取用户信息
register.setNickname(jsonObject.getString("name"));
register.setGender("m".equals(jsonObject.getString("gender")) ?1:0);
register.setProfileImageUrl(jsonObject.getString("profile_image_url"));
}
}catch (Exception e){ }
register.setSocialUid(socialUserTo.getUid());
register.setAccessToken(socialUserTo.getAccessToken());
register.setCreateTime(new Date());
userDao.insert(register);
return register;
}
}
}
- 用户实体类
@Data
@TableName("user")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId
private Long id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String profileImageUrl;
/**
* 性别
*/
private Integer gender;
/**
* 注册时间
*/
private Date createTime;
/**
* 社交id
*/
private String socialUid;
/**
* 社交令牌
*/
private String accessToken;
}
4. 登录成功,跳转到主页
<div>
<h1>
欢迎:<img th:if="${session.loginUser != null}" th:src="${session.loginUser.profileImageUrl}"/>[[${session.loginUser.nickname}]]
</h1>
</div>