OAuth: :
OAuth2.0 :
授权流程图示:
以微博为例 官方地址
引导用户到微博的认证地址
https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
用户同意授权重定向到上面设置的地址并携带 code
http://www.achangmall.com/success?code=CODE
使用 code 请求微博提供的地址换取 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:APP KEY;
client_secret:APP SECRET;
redirect_uri:认证后重定向的地址 http://www.achangmall.com/success
code:第二步返回的 code 值;
返回响应报文
{ "access_token": "2.00pDpxyGd3J5bEef6b98778e0ZKsu4", "remind_in": "157679999", "expires_in": 157679999, "uid": "6397634785", "isRealName": "true" }
根据 access_token 可以获取微博提供的公共接口数据
@Data public class SocialUser { private String access_token; private String remind_in; private long expires_in; private String uid; private String isRealName; }
@Slf4j @Controller public class OAuth2Controller { @Autowired private MemberFeignService memberFeignService; @GetMapping(value = "/oauth2.0/weibo/success") public String weibo(@RequestParam("code") String code, HttpSession session) throws Exception { Map<String, String> map = new HashMap<>(); map.put("client_id","2077705774"); map.put("client_secret","40af02bd1c7e435ba6a6e9cd3bf799fd"); map.put("grant_type","authorization_code"); map.put("redirect_uri","http://auth.achangmall.com/oauth2.0/weibo/success"); map.put("code",code); //1、根据用户授权返回的code换取access_token HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<>(), map, new HashMap<>()); //2、处理 if (response.getStatusLine().getStatusCode() == 200) { //获取到了access_token,转为通用社交登录对象 String json = EntityUtils.toString(response.getEntity()); //String json = JSON.toJSONString(response.getEntity()); SocialUser socialUser = JSON.parseObject(json, SocialUser.class); //知道了哪个社交用户 //1)、当前用户如果是第一次进网站,自动注册进来(为当前社交用户生成一个会员信息,以后这个社交账号就对应指定的会员) //登录或者注册这个社交用户 System.out.println(socialUser.getAccess_token()); //调用远程服务 R oauthLogin = memberFeignService.oauthLogin(socialUser); if (oauthLogin.getCode() == 0) { MemberResponseVo data = oauthLogin.getData("data", new TypeReference<MemberResponseVo>() {}); log.info("登录成功:用户信息:{}",data.toString()); //1、第一次使用session,命令浏览器保存卡号,JSESSIONID这个cookie //以后浏览器访问哪个网站就会带上这个网站的cookie //TODO 1、默认发的令牌。当前域(解决子域session共享问题) //TODO 2、使用JSON的序列化方式来序列化对象到Redis中 session.setAttribute("userInfo",data); //2、登录成功跳回首页 return "redirect:http://achangmall.com"; } else { return "redirect:http://auth.achangmall.com/login.html"; } } else { return "redirect:http://auth.achangmall.com/login.html"; } } }
/** * 社交登录UID */ private String socialUid; /** * 社交登录TOKEN */ private String accessToken; /** * 社交登录过期时间 */ private long expiresIn;
@PostMapping(value = "/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(); } }
@Override public MemberEntity login(SocialUser socialUser) throws Exception { //具有登录和注册逻辑 String uid = socialUser.getUid(); //1、判断当前社交用户是否已经登录过系统 MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", uid)); if (memberEntity != null) { //这个用户已经注册过 //更新用户的访问令牌的时间和access_token MemberEntity update = new MemberEntity(); update.setId(memberEntity.getId()); update.setAccessToken(socialUser.getAccess_token()); update.setExpiresIn(socialUser.getExpires_in()); this.baseMapper.updateById(update); memberEntity.setAccessToken(socialUser.getAccess_token()); memberEntity.setExpiresIn(socialUser.getExpires_in()); return memberEntity; } else { //2、没有查到当前社交用户对应的记录我们就需要注册一个 MemberEntity register = new MemberEntity(); //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"); String profileImageUrl = jsonObject.getString("profile_image_url"); register.setNickname(name); register.setGender("m".equals(gender)?1:0); register.setHeader(profileImageUrl); register.setCreateTime(new Date()); register.setSocialUid(socialUser.getUid()); register.setAccessToken(socialUser.getAccess_token()); register.setExpiresIn(socialUser.getExpires_in()); //把用户信息插入到数据库中 this.baseMapper.insert(register); } return register; } }
通过对请求ip进行hash后获取对应值,根据值来让这个ip访问某个服务的时候,都对一个服务进行访问,保证都在一个服务的session
当浏览器请求服务之后,服务会返回浏览器一个卡信息,并指定放大域名
,我们并将Session信息存储到redis中,下次,让浏览器请求我们所有的服务的时候都携带卡,每个服务都去redis去获取对应这个卡的Session信息,这样子就可以解决服务之间共享Session的问题
接下来我们通过:SpringSession
来实现redis+session共享问题