第三方服务
第三方服务
登录

模拟发送验证码
@Override public Result sendCode(String phone, HttpSession session) { // 1.校验手机号 if (RegexUtils.isPhoneInvalid(phone)) { // 2.如果不符合,返回错误信息 return Result.fail("手机号格式错误!"); } // 3.符合,生成验证码 String code = RandomUtil.randomNumbers(6); // 4.保存验证码到 session session.setAttribute("code",code); // 5.发送验证码 log.debug("发送短信验证码成功,验证码:{}", code); // 返回ok return Result.ok(); }登录
@Override public Result login(LoginFormDTO loginForm, HttpSession session) { // 1.校验手机号 String phone = loginForm.getPhone(); if (RegexUtils.isPhoneInvalid(phone)) { // 2.如果不符合,返回错误信息 return Result.fail("手机号格式错误!"); } // 3.校验验证码 Object cacheCode = session.getAttribute("code"); String code = loginForm.getCode(); if(cacheCode == null || !cacheCode.toString().equals(code)){ //3.不一致,报错 return Result.fail("验证码错误"); } //一致,根据手机号查询用户 User user = query().eq("phone", phone).one(); //5.判断用户是否存在 if(user == null){ //不存在,则创建 user = createUserWithPhone(phone); } //7.保存用户信息到session中 session.setAttribute("user",user); return Result.ok(); }登录拦截
tomcat的运行原理:当用户发起请求时,会访问我们像tomcat注册的端口,任何程序想要运行,都需要有一个线程对当前端口号进行监听,tomcat也不例外,当监听线程知道用户想要和tomcat连接连接时,那会由监听线程创建socket连接,socket都是成对出现的,用户通过socket互相传递数据,当tomcat端的socket接受到数据后,此时监听线程会从tomcat的线程池中取出一个线程执行用户请求,在我们的服务部署到tomcat后,线程会找到用户想要访问的工程,然后用这个线程转发到工程中的controller,service,dao中,并且访问对应的DB,在用户执行完请求后,再统一返回,再找到tomcat端的socket,再将数据写回到用户端的socket,完成请求和响应
通过以上讲解,我们可以得知 每个用户其实对应都是去找tomcat线程池中的一个线程来完成工作的, 使用完成后再进行回收,既然每个请求都是独立的,所以在每个用户去访问我们的工程时,我们可以使用threadlocal来做到线程隔离,每个线程操作自己的一份数据
关于threadlocal:在threadLocal中,无论是他的put方法和他的get方法, 都是先从获得当前用户的线程,然后从线程中取出线程的成员变量map,只要线程不一样,map就不一样,所以可以通过这种方式来做到线程隔离

1653068196656 public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //1.获取session HttpSession session = request.getSession(); //2.获取session中的用户 Object user = session.getAttribute("user"); //3.判断用户是否存在 if(user == null){ //4.不存在,拦截,返回401状态码 response.setStatus(401); return false; } //5.存在,保存用户信息到Threadlocal UserHolder.saveUser((User)user); //6.放行 return true; } }@Configuration public class MvcConfig implements WebMvcConfigurer { @Resource private StringRedisTemplate stringRedisTemplate; @Override public void addInterceptors(InterceptorRegistry registry) { // 登录拦截器 registry.addInterceptor(new LoginInterceptor()) .excludePathPatterns( "/shop/**", "/voucher/**", "/shop-type/**", "/upload/**", "/blog/hot", "/user/code", "/user/login" ).order(1); // token刷新的拦截器 registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(0); } }隐藏用户敏感信息
注册
LoginController
@Controller public class LoginController { @Autowired ThirdPartFeignService thirdPartFeignService; @Autowired StringRedisTemplate redisTemplate; @Autowired MemberFeignService memberFeignService; @ResponseBody @GetMapping("/sms/sendCode") public R sendCode(@RequestParam("phone") String phone) { //TODO 1、接口防刷 String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone); if (redisCode != null) { String[] splitCode = redisCode.split("_"); if (System.currentTimeMillis() - Long.parseLong(splitCode[1]) > 60000) { return R.error(BizCodeEnume.SMS_CODE_EXCEPTION.getCode(), BizCodeEnume.SMS_CODE_EXCEPTION.getMsg()); } } String code = UUID.randomUUID().toString().substring(0, 5); redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, code + "_" + System.currentTimeMillis(), 10, TimeUnit.MINUTES); // thirdPartFeignService.sendCode(phone, code); return R.ok(); } /** * RedirectAttributes:模拟重定向携带数据 * @param vo * @param result * @param redirectAttributes * @return */ @PostMapping("/regist") public String regist(@Valid UserRegistVo vo, BindingResult result, RedirectAttributes redirectAttributes){ if (result.hasErrors()){ Map<String,String> errors = result.getFieldErrors().stream().collect(Collectors.toMap(FieldError::getField,FieldError::getDefaultMessage)); // model.addAttribute("errors",errors); redirectAttributes.addFlashAttribute("errors",errors); //校验出错,转发到注册页 return "redirect:http://auth.gulimall.com/reg.html"; } //真正注册。调用远程服务进行注册 String code = vo.getCode(); String s = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone()); if (!StringUtils.isEmpty(s)){ if (code.equals(s.split("_")[0])){ //验证码通过 调用member远程服务注册 R r = memberFeignService.regist(vo); //删除验证码 redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + vo.getPhone()); if (r.getCode() == 0){ //注册成功回到首页,回到登录页 return "redirect:http://auth.gulimall.com/login.html"; }else { Map<String,String> errors = new HashMap<>(); errors.put("msg", (String) r.getData("msg",new TypeReference<String>(){})); redirectAttributes.addFlashAttribute("errors",errors); return "redirect:http://auth.gulimall.com/reg.html"; } }else { Map<String,String> errors = new HashMap<>(); errors.put("code","验证码错误"); redirectAttributes.addFlashAttribute("errors",errors); return "redirect:http://auth.gulimall.com/reg.html"; } }else { Map<String,String> errors = new HashMap<>(); errors.put("code","验证码错误"); redirectAttributes.addFlashAttribute("errors",errors); return "redirect:http://auth.gulimall.com/reg.html"; } } @PostMapping("/login") public String login(UserLoginVo vo,RedirectAttributes redirectAttributes){ //远程登录 R login = memberFeignService.login(vo); if (login.getCode() == 0){ //成功 return "redirect:http://gulimall.com"; }else { Map<String,String> errors = new HashMap<>(); errors.put("msg",(String) login.getData("msg",new TypeReference<String>(){})); redirectAttributes.addFlashAttribute("errors",errors); return "redirect:http://auth.gulimall.com/login.html"; } } }MemberController
@RestController @RequestMapping("member/member") public class MemberController { @Autowired private MemberService memberService; @PostMapping("/regist") public R regist(@RequestBody MemberRegistVo vo) { try { memberService.regist(vo); } catch (UsernameExistException e) { return R.error(BizCodeEnume.USER_EXIST_EXCEPTION.getCode(), BizCodeEnume.USER_EXIST_EXCEPTION.getMsg()); } catch (PhoneExistException e) { return R.error(BizCodeEnume.PHONE_EXIST_EXCEPTION.getCode(), BizCodeEnume.PHONE_EXIST_EXCEPTION.getMsg()); } return R.ok(); } @PostMapping("/login") public R login(@RequestBody MemberLoginVo vo){ MemberEntity entity = memberService.login(vo); if (entity!=null){ return R.ok(); } return R.error(BizCodeEnume.LOGINACTT_PASSWORD_ERROR.getCode(), BizCodeEnume.LOGINACTT_PASSWORD_ERROR.getMsg()); } }service
@Service("memberService") public class MemberServiceImpl extends ServiceImpl<MemberDao, MemberEntity> implements MemberService { @Autowired MemberLevelDao memberLevelDao; //注册 @Override public void regist(MemberRegistVo vo) { MemberDao memberDao = this.baseMapper; MemberEntity entity = new MemberEntity(); //设置默认等级 MemberLevelEntity levelEntity = memberDao.getDefaultLevel(); entity.setLevelId(levelEntity.getId()); //检查用户名和手机号是否唯一 checkPhoneUnique(vo.getPhone()); /* * Integer mobile = memberDao.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone)); * if (mobile > 0){ throw new PhoneExistException(); } */ checkUsernameUnique(vo.getUserName()); entity.setMobile(vo.getPhone()); entity.setUsername(vo.getUserName()); //密码要进行加密存储 BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String encode = passwordEncoder.encode(vo.getPassword()); entity.setPassword(encode); //其它默认信息 //保存 memberDao.insert(entity); } //登录 @Override public MemberEntity login(MemberLoginVo vo) { String loginacct = vo.getLoginacct(); String password = vo.getPassword(); MemberDao baseMapper = this.baseMapper; MemberEntity entity = baseMapper.selectOne(new QueryWrapper<MemberEntity>(). eq("username", loginacct).or().eq("mobile",loginacct)); if (entity!=null){ //1、获取数据库中存储的密码 String passwordDb = entity.getPassword(); BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); //2、密码匹配 boolean matches = passwordEncoder.matches(password, passwordDb); if (matches){ return entity; } } return null; } }
加密
@Test
void contextLoads() {
//MD5加密 会被彩虹表暴力破解
System.out.println(DigestUtils.md5Hex("123456"));
//盐值加密 需要盐值
System.out.println(Md5Crypt.md5Crypt("123456".getBytes(),"$1$qwert"));
//最终加密方式
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode("123456"));
boolean matches = passwordEncoder.matches("123456", "$2a$10$HYCiKQzLG/Q8ayGpXKtiDOovbOiohUbgrKe8FDm91B2/U5OhzoPk2");
System.out.println(matches);
}
短信验证码
阿里
参考[阿里云 OpenAPI 开发者门户 (aliyun.com)](https://next.api.aliyun.com/api/Dysmsapi/2017-05-25/SendSms?spm=api-workbench.SDK Document.0.0.57241e0fXDWJRz&lang=JAVAASYNC)
依赖
<dependency> <groupId>com.aliyun</groupId> <artifactId>alibabacloud-dysmsapi20170525</artifactId> <version>2.0.22</version> </dependency>测试代码
public class SendSms { public static void main(String[] args) throws Exception { // HttpClient Configuration /*HttpClient httpClient = new ApacheAsyncHttpClientBuilder() .connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds .responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds .maxConnections(128) // Set the connection pool size .maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds // Configure the proxy .proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001)) .setCredentials("<your-proxy-username>", "<your-proxy-password>")) // If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true)) .x509TrustManagers(new X509TrustManager[]{}) .keyManagers(new KeyManager[]{}) .ignoreSSL(false) .build();*/ // Configure Credentials authentication information, including ak, secret, token StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder() .accessKeyId("<your-accessKeyId>") .accessKeySecret("<your-accessKeySecret>") //.securityToken("<your-token>") // use STS token .build()); // Configure the Client AsyncClient client = AsyncClient.builder() .region("cn-hangzhou") // Region ID //.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient) .credentialsProvider(provider) //.serviceConfiguration(Configuration.create()) // Service-level configuration // Client-level configuration rewrite, can set Endpoint, Http request parameters, etc. .overrideConfiguration( ClientOverrideConfiguration.create() .setEndpointOverride("dysmsapi.aliyuncs.com") //.setConnectTimeout(Duration.ofSeconds(30)) ) .build(); // Parameter settings for API request SendSmsRequest sendSmsRequest = SendSmsRequest.builder() // Request-level configuration rewrite, can set Http request parameters, etc. // .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders())) .build(); // Asynchronously get the return value of the API request CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest); // Synchronously get the return value of the API request SendSmsResponse resp = response.get(); System.out.println(new Gson().toJson(resp)); // Asynchronous processing of return values /*response.thenAccept(resp -> { System.out.println(new Gson().toJson(resp)); }).exceptionally(throwable -> { // Handling exceptions System.out.println(throwable.getMessage()); return null; });*/ // Finally, close the client client.close(); } }
腾讯
依赖
<!-- 版本在maven生效需要时间,如获取不到对应的版本,可以调低版本号 --> <dependency> <groupId>com.tencentcloudapi</groupId> <artifactId>tencentcloud-sdk-java-sms</artifactId> <version>3.1.621</version> </dependency>测试类
public class SendSms { public static void main(String [] args) { try{ // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密 // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取 Credential cred = new Credential("AKIDDH5cssb2C6j6C0YppY1C6jW3JI0RbU6S", "6vSjjRt4ybx1oKZXFgqjLhma7NM4VgaD"); // 实例化一个http选项,可选的,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); httpProfile.setEndpoint("sms.tencentcloudapi.com"); // 实例化一个client选项,可选的,没有特殊需求可以跳过 ClientProfile clientProfile = new ClientProfile(); clientProfile.setHttpProfile(httpProfile); // 实例化要请求产品的client对象,clientProfile是可选的 SmsClient client = new SmsClient(cred, "ap-nanjing", clientProfile); // 实例化一个请求对象,每个接口都会对应一个request对象 SendSmsRequest req = new SendSmsRequest(); req.setSmsSdkAppId("1400758918"); req.setSignName("博途小站"); req.setTemplateId("1594298"); // 验证码 Random random = new Random(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 4; i++) { sb.append(random.nextInt(10)); } String code = sb.toString(); String[] templateParamSet = {code}; req.setTemplateParamSet(templateParamSet); // 手机号 String[] phoneNumberSet = {"+8617731974644"}; req.setPhoneNumberSet(phoneNumberSet); // 返回的resp是一个SendSmsResponse的实例,与请求对象对应 SendSmsResponse resp = client.SendSms(req); // 输出json格式的字符串回包 System.out.println(SendSmsResponse.toJsonString(resp)); } catch (TencentCloudSDKException e) { System.out.println(e.toString()); } } }
邮箱验证码
一般邮件
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>配置
spring.mail.host=smtp.qq.com #发送者邮箱 spring.mail.username=1159453866@qq.com #发送者邮箱授权值 spring.mail.password=gckbrlivpgdsfhig spring.mail.default-encoding=utf-8service
public interface EmailService { /** * toEmail 接收验证码的邮箱 * text 主题 * message 主体信息 * */ public boolean sendEmail(String toEmail, String text, String message); }@Service public class EmailServiceImpl implements EmailService{ @Resource private JavaMailSender javaMailSender; @Value("${spring.mail.username}") private String fromEmail; @Override public boolean sendEmail(String toEmail, String text, String message) { SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); //设置发件邮箱 simpleMailMessage.setFrom(fromEmail); //收件人邮箱 simpleMailMessage.setTo(toEmail); //主题标题 simpleMailMessage.setSubject(text); //信息内容 simpleMailMessage.setText(message); //执行发送 try {//发送可能失败 javaMailSender.send(simpleMailMessage); //没有异常返回true,表示发送成功 return true; } catch (Exception e) { //发送失败,返回false return false; } } }controller
@RestController @RequestMapping("/mail") public class Mail { @Resource private EmailService emailService; /** * 获取验证码 */ @ResponseBody @RequestMapping("getEmailCode") //通过httpsession来存入验证码值 public String getEmail(@RequestParam String toEmail, HttpSession httpSession) { Random random = new Random(); //生成随机验证码 int code = 1000 + random.nextInt(8999); //把验证码存储到session中 httpSession.setAttribute("code", code); //执行发送验证码 if (emailService.sendEmail(toEmail, "验证码", "欢迎注册,您的验证码为:" + code)) { return "获取成功" + code; } return "获取失败"; } /** * 校验验证码 */ @RequestMapping("checkEmailCode") @ResponseBody public String checkEmailCode(@RequestParam String code, HttpSession httpSession) { String emailCode = httpSession.getAttribute("code").toString(); if (emailCode != null) { if (emailCode.equals(code)) { return "校验成功"; } } return "校验失败"; } }
其他附件邮件
配置文件
#附件信息 spring: freemarker: template-loader-path: classpath:/templates/ suffix: .ftl cache: false charset: UTF-8 check-template-location: true content-type: text/html mail: host: smtp.163.com #发送邮件服务器 username: test@163.com #发送邮件的邮箱地址 password: SMSRDEQCIUKYODOR #客户端授权码,不是邮箱密码,网易的是自己设置的 properties.mail.smtp.port: 465 #465或者994 properties.mail.smtp.auth: 465 properties.mail.smtp.ssl.enable: true properties.mail.starttls.enable: true properties.mail.starttls.required: true from: test@163.com ## 发送邮件的地址,和上面username一致 default-encoding: UTF-8 #以下可以配置或者不配置 ## properties: ## mail: ## smtp: ## port: 465 #端口号465或994 ## auth: true ## starttls: ## enable: true ## required: trueverifyCode.ftl配置文件
<p><strong style="color: #000000; font-size: 24px;">亲爱的外规数据平台用户:</strong></p> <p>您好!</p> <p>您已成功提交注册信息 ,请在验证码输入框中输入:<b style="color: red">${verifyCode}</b>以完成注册</p>service
public interface IMailService { /** * 发送文本邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ void sendSimpleMail(String to, String subject, String content); /** * 发送HTML邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ void sendHtmlMail(String to, String subject, String content); /** * 发送带附件的邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 * @param filePath 附件 */ void sendAttachmentsMail(String to, String subject, String content, String filePath); /** * 发送模板邮件 * * @param to 收件人 * @param subject 主题 * @param fileName 邮件模板文件名称 * @param model 邮件数据载体 */ void sendModelMail(String to, String subject, String fileName, Object model); }@Slf4j @Service public class IMailServiceImpl implements IMailService { /** * Spring Boot 提供了一个发送邮件的简单抽象,使用的是下面这个接口,这里直接注入即可使用 */ @Autowired JavaMailSender mailSender; @Autowired Configuration configuration; /** * 配置文件中我的qq邮箱 */ @Value("${spring.mail.from}") private String from; /** * 简单文本邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ @Override public void sendSimpleMail(String to, String subject, String content) { //创建SimpleMailMessage对象 SimpleMailMessage message = new SimpleMailMessage(); //邮件发送人 message.setFrom(from); message.setCc(from); //邮件接收人 message.setTo(to); //邮件主题 message.setSubject(subject); //邮件内容 message.setText(content); //发送邮件 mailSender.send(message); } /** * html邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 */ @Override public void sendHtmlMail(String to, String subject, String content) { //获取MimeMessage对象 MimeMessage message = mailSender.createMimeMessage(); MimeMessageHelper messageHelper; try { messageHelper = new MimeMessageHelper(message, true); //邮件发送人 messageHelper.setFrom(from); //邮件接收人 messageHelper.setTo(to); //邮件主题 message.setSubject(subject); //邮件内容,html格式 messageHelper.setText(content, true); //发送 mailSender.send(message); //日志信息 log.info("邮件已经发送..."); } catch (MessagingException e) { log.error("发送邮件时发生异常!", e); } } /** * 带附件的邮件 * * @param to 收件人 * @param subject 主题 * @param content 内容 * @param filePath 附件 */ @Override public void sendAttachmentsMail(String to, String subject, String content, String filePath) { MimeMessage message = mailSender.createMimeMessage(); try { MimeMessageHelper helper = new MimeMessageHelper(message, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); helper.setText(content, true); //FileSystemResource file = new FileSystemResource(new File(filePath)); ClassPathResource resource = new ClassPathResource(filePath); FileSystemResource file = new FileSystemResource(resource.getFile()); helper.addAttachment(Objects.requireNonNull(file.getFilename()), file); //可以同时添加多个附件,只需要在这里直接添加第2,第3...附件就行了. //helper.addAttachment(fileName2, file2); mailSender.send(message); //日志信息 log.info("邮件已经发送..."); } catch (MessagingException e) { log.error("发送邮件时发生异常!", e); } catch (IOException e) { e.printStackTrace(); log.error("发送邮件时发生异常!", e); } } @Override public void sendModelMail(String to, String subject, String fileName, Object model) { MimeMessage mimeMessage = mailSender.createMimeMessage(); try { MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); helper.setFrom(from); helper.setTo(to); helper.setSubject(subject); Template template = configuration.getTemplate(fileName); String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model); helper.setText(html, true); mailSender.send(mimeMessage); //日志信息 log.info("邮件已经发送..."); } catch (MessagingException e) { log.error("发送邮件时发生异常!", e); } catch (TemplateException e) { e.printStackTrace(); log.error("发送邮件时发生异常!", e); } catch (IOException e) { e.printStackTrace(); } } }controller
@RestController @Api(value = "邮件模块", description = "邮件模块", tags = "邮件模块") @RequestMapping("/comm/mail/user") public class TMailController { @Autowired IMailService iMailService; @Autowired RedisTemplate redisTemplate; @PostMapping("/verifyCode") @ApiOperation(value = "获取邮箱验证码", notes = "获取邮箱验证码") public Response sendVerifyCode(@RequestBody SysUserVerifyCodeReq sysUserVerifyCodeReq) { Response response = new Response(); String key = CommConstant.USER_VERIFY_CODE + sysUserVerifyCodeReq.getUsername(); String verifyCode = (String) redisTemplate.opsForValue().get(key); if (StrUtil.isNotBlank(verifyCode)) { response.setCode(200); response.setErr_msg("请勿重复获取"); return response; } String value = RandomUtil.randomNumbers(6); redisTemplate.opsForValue().set(key, value, 3L, TimeUnit.MINUTES); String mail = sysUserVerifyCodeReq.getMail(); MailModel mailModel = new MailModel(); mailModel.setVerifyCode(value); iMailService.sendModelMail(mail,"系统验证码","verifyCode.ftl",mailModel); response.setCode(200); return response; } }
社交登录
OAuth2.0?
OAuth是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供非第三方网站或分享他们数据的所有内容。
' 简而言之 ' 就是我要登 ' 你 ' 的网站,但是我不想注册,我想直接用 ' 别的网站 ' 的账号密码登录,而你只要确定我的确有别的网站的账号就行了。
而OAuth2.0加上了对用户相关的OpenAPI的数据获取都需要显示的向用户征求授权(比如向你询问要你的头像、id或其他的数据保存)。可以说一代为基础,二代为规范。
社交登录流程
1、用户发起社交登录请求,其它社交网站向用户申请请求认证 2、用户授权(输入自己的社交账号密码) 3、其它社交网站使用上一步的授权,进行认证 4、认证通过,给正在登录的网站返回访问令牌 5、正在登录的网站使用访问令牌,获取一些用户在其它社交网站的开放的保护信息 6、其它社交网站认证令牌,返回信息
