社交登录流程

407 阅读4分钟

社交登录,是指用户可以使用微博、QQ、微信等社交媒体账号登录网站。从用户的角度来看,社交登录不仅可以省去注册账号的操作过程,也可以不用记忆账号和密码,网站接入社交登录功能是真的香。该如何接入社交登录呢?接入流程是怎样的呢?别急,且听我慢慢道来。

一、环境搭建

1. 申请微博登录接口

以微博登录为例,你首先需要到微博开放平台申请一个网页应,填写应哟用相关信息。

等待审核通过,就可以获得一个App Key和App Secret,你需要设置授权回调页的地址,现在你就可以使用微博登入接口了。

2 社交登录流程

在微博开放平台有关于OAuth2.0协议的授权流程图。

web网站授权流程.gif

我来带大家解读一下这个授权流程。

微博登录流程.png

  1. 首先点击微博登录按钮,需要携带client_id和回调地址(必须和微博开放平台上设置的授权回调页地址相同)到如下地址 :

    https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
    
  2. 用户使用微博账号和密码进行授权,授权成功,重定向到:

    YOUR_REGISTERED_REDIRECT_URI/?code=CODE
    
  3. 接下来带着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码

    返回数据:

  1. 认证服务器验证通过,返回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. 处理登录请求

  1. 首先获取到请求参数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";
            }
        }
    }
    
  2. 用户接收换取Access Token的请求返回数据的实体类

    @Data
    public class SocialUserTo {
    
    
        private String accessToken;
    
        private long expiresIn;
    
        private String refreshToken;
    
        private String uid;
    
        private String remindIn;
    
    
    }
    

3. 用户登录

  1. 如果用户已经注册,直接登录;如果用户未进行注册,则拿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;
            }
        }
    }
    
  2. 用户实体类

    @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>

7CW270ISB}IK$R7N)BOUI{Q.png