社交登录,是指用户可以使用微博、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>