QQ、微博、微信、github等网站的用户量非常大,如果让用户用我们的网站,按照我们的注册流程来走,用户可能嫌太麻烦,这样就会损失用户。
步骤
- 用户点击注册页的微信按钮
- 引导跳转到微信授权页
- 用户主动点击授权,跳回之前网页todo这儿应该是跳到登录页吧
社交登录遵循的是OAuth2.0协议
OAuth2.0协议
-
OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容。
-
OAuth2.0:对于用户相关的 OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。
这里的第三方应用代表的就是我们的应用。
微博登录
本来想调用微信登录的,但是看了一下才觉得好麻烦,需要审核网站的资质、备案等等,即使它和微博一样,也有开发的这个调用阶段
,那也不太好找,太浪费时间了。
所以还是跟着老师,做一遍微博登录。
微博信息注册
微博调用接口有开发阶段,不需要网站上线等
使用微博登录微博开放平台,进行身份认证,我通过的时间大概是24小时。
只需要填写下面的基本信息和高级信息。
授权回调页和取消,是我们微博登录成功后需要跳转我们的页面,成功的页面或者失败的页面。
微博OAuth2.0登录
- Client(第三方应用,gulimall)给User Agent(用户代理,浏览器)发请求,相当于在第三方网站点了微博登录的按钮
- 浏览器携带客户端的标识和重定向的地址,还有用户的认证信息(账号密码),去向微博认证服务器进行认证,认证成功会跳转回浏览器,并给出一个授权码。
- 浏览器会将授权码给到第三方网站,第三方网站拿着授权码和重定向地址,向微博认证服务器交换一个访问令牌,第三方网站就拿着访问令牌来调用微博的api。
获取微博Access Token
- 引导需要授权的用户到如下地址:
// client_id 上面的 App Key
// redirect_uri 上面配置的授权回调页
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
换取Access Token- 一个CODE只能换取一次Access Token
- 同一个用户的Access Token在一段时间是不会变化的,即使用多个不同的code获取
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=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET可以使用basic方式加入header中,返回值
{
"access_token": "ASFDFDGDF",
"remind_in": 3600,
"expires_in": 3600
}
- 使用获得的Access Token调用API
微博登录code
- 添加HttpUtils工具类
controller
package com.atlinxi.gulimall.authserver.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atlinxi.common.utils.HttpUtils;
import com.atlinxi.common.utils.R;
import com.atlinxi.gulimall.authserver.feign.MemberFeignService;
import com.atlinxi.gulimall.authserver.vo.MemberRespVo;
import com.atlinxi.gulimall.authserver.vo.SocialUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.HashMap;
import java.util.Map;
/**
* 处理社交登录请求
*/
@Controller
@Slf4j
public class OAuth2Controller {
@Autowired
MemberFeignService memberFeignService;
@GetMapping("/auth2.0/weibo/success")
public String weibo(@RequestParam("code") String code) throws Exception {
// 1. 根据登录微博返回的code换取accessToken
Map<String,String> map = new HashMap<>();
map.put("client_id","1657382705");
map.put("client_secret","ca422dd82a99de49026332a9ecf6a7c4");
map.put("grant_type","authorization_code");
map.put("redirect_uri","http://auth.gulimall.com/auth2.0/weibo/success");
map.put("code",code);
HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post",
null, null, map);
if (response.getStatusLine().getStatusCode()==200){
// 获取到了accessToken
String json = EntityUtils.toString(response.getEntity());
SocialUser socialUser = JSON.parseObject(json, SocialUser.class);
// 知道了当前是哪个微博用户
// 1. 当前用户如果是第一次进网站,自动注册进来(为当前社交用户生成一个会员信息账号,
// 以后这个社交账号就对应我们指定的会员)
R oauthLogin = memberFeignService.oauthLogin(socialUser);
if (oauthLogin.getCode()==0){
// 2. 登录成功就跳回首页
MemberRespVo data = oauthLogin.getData(new TypeReference<MemberRespVo>() {
});
System.out.println("登录成功:用户信息" + data);
log.info("登录成功:用户信息:{}",data.toString());
return "redirect:http://gulimall.com";
}else {
return "redirect:http://auth.gulimall.com/login.html";
}
// 登录或者注册这个社交用户
}else {
return "redirect:http://auth.gulimall.com/login.html";
}
}
}
package com.atlinxi.gulimall.member.controller;
import java.util.Arrays;
import java.util.Map;
import com.atlinxi.common.exception.BizCodeEnume;
import com.atlinxi.gulimall.member.exception.PhoneExistException;
import com.atlinxi.gulimall.member.exception.UsernameExistException;
import com.atlinxi.gulimall.member.feign.CouponFeignService;
import com.atlinxi.gulimall.member.vo.MemberLoginVo;
import com.atlinxi.gulimall.member.vo.MemberRegistVo;
import com.atlinxi.gulimall.member.vo.SocialUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import com.atlinxi.gulimall.member.entity.MemberEntity;
import com.atlinxi.gulimall.member.service.MemberService;
import com.atlinxi.common.utils.PageUtils;
import com.atlinxi.common.utils.R;
/**
* 会员
*
* @author linxi
* @email [email protected]
* @date 2022-10-13 17:20:57
*/
@RefreshScope
@RestController
@RequestMapping("member/member")
public class MemberController {
@Autowired
private MemberService memberService;
@Autowired
CouponFeignService couponFeignService;
@Value("${member.name}")
private String memberName;
@Value("${member.age}")
private String memberAge;
@PostMapping("/oauth2/login")
public R oauthLogin(@RequestBody SocialUser socialUser) throws Exception {
MemberEntity memberEntity = memberService.login(socialUser);
if (memberEntity!=null){
return R.ok().setData(memberEntity);
}else {
return R.error(BizCodeEnume.LOGINACCT_PASSWORD_INVALID_EXCEPTION.getCode(),
BizCodeEnume.LOGINACCT_PASSWORD_INVALID_EXCEPTION.getMessage());
}
}
service
@Override
public MemberEntity login(SocialUser socialUser) throws Exception {
// 登录和注册合并逻辑
String uid = socialUser.getUid();
// 1. 判断当前社交用户是否已经登陆过系统
MemberEntity memberEntity = baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", uid));
if (memberEntity!=null){
// 这个用户已经注册了
MemberEntity update = new MemberEntity();
update.setId(memberEntity.getId());
update.setAccessToken(socialUser.getAccess_token());
update.setExpiresIn(socialUser.getExpires_in());
baseMapper.updateById(update);
memberEntity.setExpiresIn(socialUser.getExpires_in());
memberEntity.setAccessToken(socialUser.getAccess_token());
return memberEntity;
}else {
// 2. 没有查到当前社交用户对应的记录,我们就需要注册一个
MemberEntity regist = new MemberEntity();
try {
// 3. 查询当前社交用户的社交账号信息(昵称,性别等)
Map<String,String> query = new HashMap<>();
query.put("access_token", socialUser.getAccess_token());
query.put("uid", socialUser.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);
// 昵称
String name = jsonObject.getString("name");
String gender = jsonObject.getString("gender");
// 获取微博返回的其他信息。。。。。。
regist.setGender("m".equals(gender)?1:0);
}
}catch (Exception e){
}
regist.setSocialUid(socialUser.getUid());
regist.setAccessToken(socialUser.getAccess_token());
regist.setExpiresIn(socialUser.getExpires_in());
baseMapper.insert(regist);
return regist;
}
}
vo
package com.atlinxi.gulimall.authserver.vo;
import lombok.Data;
@Data
public class SocialUser {
private String access_token;
private String remind_in;
private long expires_in;
private String uid;
private String isRealName;
}
package com.atlinxi.gulimall.authserver.vo;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
@Data
@ToString
public class MemberRespVo {
/**
* id
*/
private Long id;
/**
* 会员等级id
*/
private Long levelId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 昵称
*/
private String nickname;
/**
* 手机号码
*/
private String mobile;
/**
* 邮箱
*/
private String email;
/**
* 头像
*/
private String header;
/**
* 性别
*/
private Integer gender;
/**
* 生日
*/
private Date birth;
/**
* 所在城市
*/
private String city;
/**
* 职业
*/
private String job;
/**
* 个性签名
*/
private String sign;
/**
* 用户来源
*/
private Integer sourceType;
/**
* 积分
*/
private Integer integration;
/**
* 成长值
*/
private Integer growth;
/**
* 启用状态
*/
private Integer status;
/**
* 注册时间
*/
private Date createTime;
private String socialUid;
private String accessToken;
private Long expiresIn;
}
BizCodeEnume
package com.atlinxi.common.exception;
/* * 错误码和错误信息定义类 *
* 1. 错误码定义规则为 5 为数字
* 2. 前两位表示业务场景,最后三位表示错误码。例如:100001。10:通用 001:系统未知 异常
* 3. 维护错误码后需要维护错误描述,将他们定义为枚举形式 *
*
* 错误码列表:
* 10: 通用
* 001:参数格式校验
* 002: 短信验证码频率太高
* 11: 商品
* 12: 订单
* 13: 购物车
* 14: 物流
* 15:用户(会员)
*
*
**/
public enum BizCodeEnume {
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VAILD_EXCEPTION(10001,"参数格式校验失败"),
SMS_CODE_EXCEPTION(10002,"短信验证码获取频率太高,稍后再试"),
PRODUCT_UP_EXCEPTION(11000,"商品上架异常"),
USER_EXIST_EXCEPTION(15001,"用户已存在"),
PHONE_EXIST_EXCEPTION(15002,"手机号已存在"),
LOGINACCT_PASSWORD_INVALID_EXCEPTION(15003,"账号或密码错误");
private int code;
private String message;
BizCodeEnume(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
小逸幼儿园毕业的时候,老师要求每位家长给孩子写一封信,当着全班同学的面念出来。不少家长写的都是感谢老师、妈妈爱你之类的常规内容。苏迎澜的标题是《天下没有不散的筵席》,“分离是为了下一次更好地聚在一起。如果你变得好了,就可以用自己的能力去团聚。”
一个妈妈的反校园暴力“战斗”
https://baijiahao.baidu.com/s?id=1760481532554271247