【第十六篇】商城系统-认证系统构建_spring boot

商城认证服务

一、搭建认证服务环境

  结合我们前面介绍的商城的架构我们需要单独的搭建一个认证服务。

【第十六篇】商城系统-认证系统构建_java_02

1.创建项目

  首先创建一个SpringBoot项目,然后添加对应的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.12</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.msb.mall</groupId>
<artifactId>mall-auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mall-auth_server</name>
<description>认证服务</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.1</spring-cloud.version>
</properties>
<dependencies>
<!-- 公共依赖 -->
<dependency>
<groupId>com.msb.mall</groupId>
<artifactId>mall-commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

2.注册中心配置

  我们需要把认证服务注册到Nacos中,添加对应的依赖,然后完成对应的配置

# Nacos服务注册
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.56.100:8848
application:
name: mall-auth_server
# 统一的全局的--设置服务器响应给客户端的日期时间格式
jackson:
date-format: yyyy-MM-dd HH:mm:ss
thymeleaf:
cache: false # Thymeleaf
server:
port: 30000

放开Nacos注册中心

【第十六篇】商城系统-认证系统构建_分布式Session_03

然后启动测试

【第十六篇】商城系统-认证系统构建_分布式Session_04

3.登录和注册页面

  然后我们整理登录和注册的相关资源,首先把登录和注册的模板文件拷贝进项目

【第十六篇】商城系统-认证系统构建_spring boot_05

然后把对应的静态文件拷贝到Nginx中。

【第十六篇】商城系统-认证系统构建_java_06

然后我们需要在host文件中添加对应的配置

【第十六篇】商城系统-认证系统构建_分布式Session_07

修改Nginx的反向代理的配置

【第十六篇】商城系统-认证系统构建_java_08

然后重启Nginx的服务

【第十六篇】商城系统-认证系统构建_分布式Session_09

然后修改网关服务

【第十六篇】商城系统-认证系统构建_java_10

最后调整登录和注册页面的静态资源文件的路径.

注册服务的名称:msb-auth,启动对应的服务,测试

登录页面

注册页面

【第十六篇】商城系统-认证系统构建_java_11

4.注册功能

4.1 手机验证码

  先处理验证码的页面,使其能够倒数操作

【第十六篇】商城系统-认证系统构建_分布式Session_12

【第十六篇】商城系统-认证系统构建_分布式Session_13

JS代码

$(function(){
$("#sendCode").click(function(){
if($(this).hasClass("d1")){
// 说明正在倒计时
}else{
// 给指定的手机号发送验证码
timeoutChangeStyle()
}

});
})
var num = 10
function timeoutChangeStyle(){
$("#sendCode").attr("class","d1")
if(num == 0){
// 说明1分钟到了,可以再次发送验证码了
$("#sendCode").text("发送验证码")
num= 10;
$("#sendCode").attr("class","")
}else{
setTimeout('timeoutChangeStyle()',1000)
$("#sendCode").text(num+"s后再次发送")
}

num --;
}

4.2 短信验证接口

  通过阿里云的短信服务来实现。我们直接购买0元15次就可以了。https://www.aliyun.com/

【第十六篇】商城系统-认证系统构建_认证_14

【第十六篇】商城系统-认证系统构建_分布式Session_15

进入到对应的管理控制台,查看对应的信息

【第十六篇】商城系统-认证系统构建_OAuth_16

通过短信供应商提供的相关的编程语言的开发模板开发即可

【第十六篇】商城系统-认证系统构建_分布式Session_17

供应商提供了对应的HttpUtils工具类,我们需要下载保存到我们自己的项目中。

【第十六篇】商城系统-认证系统构建_认证_18

然后封装对应的发送验证码的接口

package com.msb.mall.third.utils;

import lombok.Data;
import org.apache.http.HttpResponse;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
* 短信组件
*/
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Data
@Component
public class SmsComponent {

private String host;
private String path;
private String method = "POST";
private String appCode;

/**
* 发送短信验证码
* @param phone 发送的手机号
* @param code 发送的短信验证码
*/
public void sendSmsCode(String phone,String code){
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appCode);
//根据API的要求,定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<String, String>();
Map<String, String> bodys = new HashMap<String, String>();
bodys.put("content", "code:"+code);
bodys.put("phone_number", phone);
bodys.put("template_id", "TPL_0000");


try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
System.out.println(response.toString());
//获取response的body
//System.out.println(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
e.printStackTrace();
}
}
}

添加对应的属性信息

【第十六篇】商城系统-认证系统构建_OAuth_19

【第十六篇】商城系统-认证系统构建_分布式Session_20

4.3 短信功能

  然后我们将短信功能串联起来

【第十六篇】商城系统-认证系统构建_认证_21

4.3.1 third服务

  我们需要在第三方服务中提供对外的接口服务

@RestController
public class SMSController {

@Autowired
private SmsComponent smsComponent;

/**
* 调用短信服务商提供的短信API发送短信
* @param phone
* @param code
* @return
*/
@GetMapping("/sms/sendcode")
public R sendSmsCode(@RequestParam("phone") String phone,@RequestParam("code") String code){
smsComponent.sendSmsCode(phone,code);
return R.ok();
}
}

【第十六篇】商城系统-认证系统构建_spring boot_22

4.3.2 认证服务

  我们需要在认证服务中通过feign来调用third中提供的短信服务,同时给客户端提供访问的接口

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

放开注解

【第十六篇】商城系统-认证系统构建_认证_23

然后声明Feign的接口

【第十六篇】商城系统-认证系统构建_java_24

定义对应的Controller

@Controller
public class LoginController {


@Autowired
private ThirdPartFeginService thirdPartFeginService;

@ResponseBody
@GetMapping("/sms/sendCode")
public R sendSmsCode(@RequestParam("phone") String phone){
// 生成随机的验证码
String code = UUID.randomUUID().toString().substring(0, 5);
thirdPartFeginService.sendSmsCode(phone,code);
return R.ok();
}
}
4.3.3 客户端

  然后我们需要在页面中通过jQuery的异步提交来发送短信

【第十六篇】商城系统-认证系统构建_java_25

4.3.4 验证码存储

  当我们把验证码通过短信的形式发送给你客户手机,然后我们需要把手机号和对应的验证码存储起来,后面可能会集群部署,这时我们把这个信息存在在Redis中。

【第十六篇】商城系统-认证系统构建_OAuth_26

添加配置

【第十六篇】商城系统-认证系统构建_分布式Session_27

存储数据

【第十六篇】商城系统-认证系统构建_OAuth_28

【第十六篇】商城系统-认证系统构建_spring boot_29


搞定

4.3.5 60秒间隔

  针对验证码发送的间隔必须是60秒以上,这时我们可以在保存到Redis中的数据的值我们加上发送的时间来处理

【第十六篇】商城系统-认证系统构建_java_30

模板页面也需要做出对应的处理

【第十六篇】商城系统-认证系统构建_OAuth_31

4.4 注册数据验证

  表单提交的注册数据我们通过JSR303来验证。

首先定义VO对象

package com.msb.mall.vo;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

/**
* 注册用户的VO对象
*/
@Data
public class UserRegisterVo {

@NotEmpty(message = "账号不能为空")
@Length(min = 3,max = 15,message = "账号必须是3~15位")
private String userName; // 账号

@NotEmpty(message = "密码不能为空")
@Length(min = 3,max = 15,message = "密码必须是3~15位")
private String password; // 密码

@NotEmpty(message = "手机号不能为空")
@Pattern(regexp = "^[1][3-9][0-9]{9}$",message = "手机号不合法")
private String phone; // 手机号

@NotEmpty(message = "验证码不能为空")
private String code; // 验证码
}

然后就是控制器的逻辑代码

@PostMapping("/sms/register")
public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){
if(result.hasErrors()){
// 表示提交的数据不合法
List<FieldError> fieldErrors = result.getFieldErrors();
Map<String,String> map = new HashMap<>();
for (FieldError fieldError : fieldErrors) {
String field = fieldError.getField();
String defaultMessage = fieldError.getDefaultMessage();
map.put(field,defaultMessage);
}
model.addAttribute("error",map);
return "/reg";
}
// 表单提交的注册的数据是合法的

return "redirect:/login.html";
}

然后就是页面代码处理

【第十六篇】商城系统-认证系统构建_OAuth_32

【第十六篇】商城系统-认证系统构建_分布式Session_33

验证码的校验

【第十六篇】商城系统-认证系统构建_spring boot_34

4.5 完整的注册功能

【第十六篇】商城系统-认证系统构建_分布式Session_35

会员服务处理

控制器

/**
* 会员注册
* @return
*/
@PostMapping("/register")
public R register(@RequestBody MemberReigerVO vo){
try {
memberService.register(vo);
}catch (UsernameExsitException exception){
return R.error(BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getCode(),
BizCodeEnume.USERNAME_EXSIT_EXCEPTION.getMsg());
}catch (PhoneExsitExecption exsitExecption) {
return R.error(BizCodeEnume.PHONE_EXSIT_EXCEPTION.getCode(),
BizCodeEnume.PHONE_EXSIT_EXCEPTION.getMsg());
}catch (Exception e){
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),
BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}

return R.ok();
}

然后对应的Service

/**
* 完成会员的注册功能
* @param vo
*/
@Override
public void register(MemberReigerVO vo) throws PhoneExsitExecption,UsernameExsitException{
MemberEntity entity = new MemberEntity();
// 设置会员等级 默认值
MemberLevelEntity memberLevelEntity = memberLevelService.queryMemberLevelDefault();
entity.setLevelId(memberLevelEntity.getId()); // 设置默认的会员等级

// 添加对应的账号和手机号是不能重复的
checkUsernameUnique(vo.getUserName());
checkPhoneUnique(vo.getPhone());

entity.setUsername(vo.getUserName());
entity.setMobile(vo.getPhone());

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encode = encoder.encode(vo.getPassword());
// 需要对密码做加密处理
entity.setPassword(encode);
// 设置其他的默认值
this.save(entity);
}

auth服务通过Fegin远程调用

【第十六篇】商城系统-认证系统构建_java_36

远程调用

@PostMapping("/sms/register")
public String register(@Valid UserRegisterVo vo, BindingResult result, Model model){
Map<String,String> map = new HashMap<>();
if(result.hasErrors()){
// 表示提交的数据不合法
List<FieldError> fieldErrors = result.getFieldErrors();

for (FieldError fieldError : fieldErrors) {
String field = fieldError.getField();
String defaultMessage = fieldError.getDefaultMessage();
map.put(field,defaultMessage);
}
model.addAttribute("error",map);
return "/reg";
}else{
// 验证码是否正确
String code = (String)redisTemplate.opsForValue().get(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());
code = code.split("_")[0];
if(!code.equals(vo.getCode())){
// 说明验证码不正确
map.put("code","验证码错误");
model.addAttribute("error",map);
return "/reg";
}else{
// 验证码正确 删除验证码
redisTemplate.delete(SMSConstant.SMS_CODE_PERFIX + vo.getPhone());
// 远程调用对应的服务 完成注册功能
R r = memberFeginService.register(vo);
if(r.getCode() == 0){
// 注册成功
return "redirect:http://msb.auth.com/login.html";
}else{
// 注册失败
map.put("msg",r.getCode()+":"+r.get("msg"));
model.addAttribute("error",map);
return "/reg";
}
}
}



//return "redirect:/login.html";
}

完成对应的服务注册

【第十六篇】商城系统-认证系统构建_分布式Session_37

校验提示

【第十六篇】商城系统-认证系统构建_OAuth_38

5.登录功能

  通过最基础的登录操作来完成登录处理

登录页面处理

【第十六篇】商城系统-认证系统构建_分布式Session_39

认证服务的处理

/**
* 注册的方法
* @return
*/
@PostMapping("/login")
public String login(LoginVo loginVo , RedirectAttributes redirectAttributes){
R r = memberFeginService.login(loginVo);
if(r.getCode() == 0){
// 表示登录成功
return "redirect:http://msb.mall.com/home";
}

redirectAttributes.addAttribute("errors",r.get("msg"));

// 表示登录失败,重新跳转到登录页面
return "redirect:http://msb.auth.com/login.html";


}

会员中心的认证逻辑

@RequestMapping("/login")
public R login(@RequestBody MemberLoginVO vo){
MemberEntity entity = memberService.login(vo);
if(entity != null){
return R.ok();
}

return R.error(BizCodeEnume.USERNAME_PHONE_VALID_EXCEPTION.getCode(),
BizCodeEnume.USERNAME_PHONE_VALID_EXCEPTION.getMsg());
}

service中的具体认证处理

@Override
public MemberEntity login(MemberLoginVO vo) {
// 1.根据账号或者手机号来查询会员信息
MemberEntity entity = this.getOne(new QueryWrapper<MemberEntity>()
.eq("username", vo.getUserName())
.or()
.eq("mobile", vo.getUserName()));
if(entity != null){
// 2.如果账号或者手机号存在 然后根据密码加密后的校验来判断是否登录成功
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
boolean matches = encoder.matches(vo.getPassword(), entity.getPassword());
if(matches){
// 表明登录成功
return entity;
}
}
return null;
}

6.Auth2.0

  OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth
2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。

【第十六篇】商城系统-认证系统构建_java_40

6.1 微博开放平台

地址:https://open.weibo.com/

【第十六篇】商城系统-认证系统构建_OAuth_41

创建应用

【第十六篇】商城系统-认证系统构建_OAuth_42

【第十六篇】商城系统-认证系统构建_spring boot_43

创建后的基本信息:

【第十六篇】商城系统-认证系统构建_java_44

授权设置:

【第十六篇】商城系统-认证系统构建_OAuth_45

社交认证文档:

【第十六篇】商城系统-认证系统构建_分布式Session_46

微博Web端授权的操作:

【第十六篇】商城系统-认证系统构建_OAuth_47


引导用户点击按钮跳转到对应的授权页面

【第十六篇】商城系统-认证系统构建_spring boot_48

【第十六篇】商城系统-认证系统构建_认证_49

点击授权按钮后查看回调接口的code信息

【第十六篇】商城系统-认证系统构建_java_50

获取到了code信息:59d62e59e5ead5a4ea89c6f9cf212568

然后根据code信息我们可以去授权服务器获取对应的AccessToken。

https://api.weibo.com/oauth2/access_token?client_id=1093598037&client_secret=1085c8de04dee49e9bb110eaf2d3cf62&grant_type=authorization_code&redirect_uri=http://msb.auth.com/success.html&code=59d62e59e5ead5a4ea89c6f9cf212568

获取Token信息只支持POST方式提交

【第十六篇】商城系统-认证系统构建_spring boot_51

在PostMan中通过post方式提交成功获取到了对应的token信息

【第十六篇】商城系统-认证系统构建_分布式Session_52

获取到了Token信息后我们就可以去资源服务器获取对象的信息

【第十六篇】商城系统-认证系统构建_认证_53

6.2 百度开放平台

地址:

Auth2.0操作:https://developer.baidu.com/wiki/index.php?title=docs/oauth

【第十六篇】商城系统-认证系统构建_spring boot_54

创建应用:http://developer.baidu.com/console#app/project

【第十六篇】商城系统-认证系统构建_分布式Session_55

【第十六篇】商城系统-认证系统构建_认证_56

创建完成:

【第十六篇】商城系统-认证系统构建_java_57

引导用户跳转到授权地址:

http://openapi.baidu.com/oauth/2.0/authorize?
response_type=code&
client_id=YOUR_CLIENT_ID&
redirect_uri=YOUR_REGISTERED_REDIRECT_URI&
scope=email&
display=popup

地址修改为我们自己的:http://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=MmvAkQM7HtrQnKDDhhmTSib5&redirect_uri=http://www.baidu.com&display=popup

【第十六篇】商城系统-认证系统构建_spring boot_58

获取到的Code信息

【第十六篇】商城系统-认证系统构建_OAuth_59

code:d789d0160b2fa99bb1f840002569526e

获取到对应的token信息

【第十六篇】商城系统-认证系统构建_分布式Session_60

Token:121.6966ae0e0f3cd19fa36a375489342b08.YmfrSxYqsOt1eUoPzkC60yCsa7W09OmqTbPsuVL.zmdMFg

token访问地址:https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser?access_token=121.6966ae0e0f3cd19fa36a375489342b08.YmfrSxYqsOt1eUoPzkC60yCsa7W09OmqTbPsuVL.zmdMFg

【第十六篇】商城系统-认证系统构建_java_61

7.社交登录实现

7.1 code处理

  在后台服务中获取code并对应的获取Token信息

【第十六篇】商城系统-认证系统构建_分布式Session_62

【第十六篇】商城系统-认证系统构建_java_63

然后需要同步的调整引入的链接地址:

【第十六篇】商城系统-认证系统构建_认证_64

7.2 获取Token信息

  根据上一步获取的code信息,我们可以获取对应的Token信息

@RequestMapping("/oauth/weibo/success")
public String weiboOAuth(@RequestParam("code") String code) throws Exception {
Map<String,String> body = new HashMap<>();
body.put("client_id","1093598037");
body.put("client_secret","1085c8de04dee49e9bb110eaf2d3cf62");
body.put("grant_type","authorization_code");
body.put("redirect_uri","http://msb.auth.com/oauth/weibo/success");
body.put("code",code);
// 根据Code获取对应的Token信息
HttpResponse post = HttpUtils.doPost("https://api.weibo.com"
, "/oauth2/access_token"
, "post"
, new HashMap<>()
, null
, body
);
int statusCode = post.getStatusLine().getStatusCode();
if(statusCode != 200){
// 说明获取Token失败,就调回到登录页面
return "redirect:http://msb.auth.com/login.html";
}
// 说明获取Token信息成功
String json = EntityUtils.toString(post.getEntity());
SocialUser socialUser = JSON.parseObject(json, SocialUser.class);
// 注册成功就需要调整到商城的首页
return "redirect:http://msb.mall.com/home.html";
}

7.3 登录和注册

  表结构中新增对应的

【第十六篇】商城系统-认证系统构建_OAuth_65

然后在对应的实体对象中添加对应的属性

【第十六篇】商城系统-认证系统构建_分布式Session_66

service中实现注册和登录的逻辑

/**
* 社交登录
* @param vo
* @return
*/
@Override
public MemberEntity login(SocialUser vo) {
String uid = vo.getUid();
// 如果该用户是第一次社交登录,那么需要注册
// 如果不是第一次社交登录 那么就更新相关信息 登录功能
MemberEntity memberEntity = this.getOne(new QueryWrapper<MemberEntity>().eq("social_uid", uid));
if(memberEntity != null){
// 说明当前用户已经注册过了 更新token和过期时间
MemberEntity entity = new MemberEntity();
entity.setId(memberEntity.getId());
entity.setAccessToken(vo.getAccessToken());
entity.setExpiresIn(vo.getExpiresIn());
this.updateById(entity);
// 在返回的登录用户信息中我们同步的也保存 token和过期时间
memberEntity.setAccessToken(vo.getAccessToken());
memberEntity.setExpiresIn(vo.getExpiresIn());
return memberEntity;
}
// 表示用户是第一提交,那么我们就需要对应的来注册
MemberEntity entity = new MemberEntity();
entity.setAccessToken(vo.getAccessToken());
entity.setExpiresIn(vo.getExpiresIn());
entity.setSocialUid(vo.getUid());
// 通过token调用微博开发的接口来获取用户的相关信息
try {
Map<String,String> querys = new HashMap<>();
querys.put("access_token",vo.getAccessToken());
querys.put("uid",vo.getUid());
HttpResponse response = HttpUtils.doGet("https://api.weibo.com"
, "/2/users/show.json"
, "get"
, new HashMap<>()
, querys
);
if(response.getStatusLine().getStatusCode() == 200){
String json = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSON.parseObject(json);
String nickName = jsonObject.getString("screen_name");
String gender = jsonObject.getString("gender");
entity.setNickname(nickName);
entity.setGender("m".equals(gender)?1:0);
}
}catch (Exception e){

}
// 注册用户信息
this.save(entity);
return entity;
}

7.4 登录的串联

在Auth服务中我们需要通过Feign来调用MemberService中的相关服务来完成最后的串联

@RequestMapping("/oauth/weibo/success")
public String weiboOAuth(@RequestParam("code") String code) throws Exception {
Map<String,String> body = new HashMap<>();
body.put("client_id","1093598037");
body.put("client_secret","1085c8de04dee49e9bb110eaf2d3cf62");
body.put("grant_type","authorization_code");
body.put("redirect_uri","http://msb.auth.com/oauth/weibo/success");
body.put("code",code);
// 根据Code获取对应的Token信息
HttpResponse post = HttpUtils.doPost("https://api.weibo.com"
, "/oauth2/access_token"
, "post"
, new HashMap<>()
, null
, body
);
int statusCode = post.getStatusLine().getStatusCode();
if(statusCode != 200){
// 说明获取Token失败,就调回到登录页面
return "redirect:http://msb.auth.com/login.html";
}
// 说明获取Token信息成功
String json = EntityUtils.toString(post.getEntity());
SocialUser socialUser = JSON.parseObject(json, SocialUser.class);
R r = memberFeginService.socialLogin(socialUser);
if(r.getCode() != 0){
// 登录错误
return "redirect:http://msb.auth.com/login.html";
}
String entityJson = (String) r.get("entity");
System.out.println("----------------->" + entityJson);
// 注册成功就需要调整到商城的首页
return "redirect:http://msb.mall.com/home";
}

二、分布式session

1.session问题

【第十六篇】商城系统-认证系统构建_OAuth_67

【第十六篇】商城系统-认证系统构建_OAuth_68

【第十六篇】商城系统-认证系统构建_分布式Session_69

【第十六篇】商城系统-认证系统构建_spring boot_70

【第十六篇】商城系统-认证系统构建_OAuth_71

【第十六篇】商城系统-认证系统构建_认证_72

2.SpringSession整合

  我们通过SpringSession来实现Session的共享,Session数据存储在Redis中

【第十六篇】商城系统-认证系统构建_java_73

SpringSession的操作指南:

​https://docs.spring.io/spring-session/docs/2.5.6/reference/html5/guides/boot-redis.html​

导入相关的依赖

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

设置对应的配置

【第十六篇】商城系统-认证系统构建_分布式Session_74

最后我们需要添加对有的注解,放开操作

【第十六篇】商城系统-认证系统构建_OAuth_75

然后在Auth服务和商城首页都整合SpringSession后,我们再商城首页可以看到Session的数据,注意这儿是手动修改Cookie的域名

【第十六篇】商城系统-认证系统构建_分布式Session_76

3.自定义Cookie

  通过自定义Cookie实现session域名的调整

@Configuration
public class MySessionConfig {

/**
* 自定义Cookie的配置
* @return
*/
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("msb.com"); // 设置session对应的一级域名
cookieSerializer.setCookieName("msbsession");
return cookieSerializer;
}

/**
* 对存储在Redis中的数据指定序列化的方式
* @return
*/
@Bean
public RedisSerializer<Object> redisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}

4.单点登录案例演示

  xxl-sso案例代码地址:https://gitee.com/xuxueli0323/xxl-sso?_from=gitee_search

下载下来的代码解压缩后通过idea导入,然后修改server和simple中的属性文件,同时我们需要在host中设置对应的域名

127.0.0.1 ssoserver.com
127.0.0.1 msb1.com
127.0.0.1 msb2.com

在server的配置文件中,修改Redis的服务地址

【第十六篇】商城系统-认证系统构建_java_77

然后在simple中修改server的地址和redis服务的地址

【第十六篇】商城系统-认证系统构建_spring boot_78

然后分别启动服务测试即可

【第十六篇】商城系统-认证系统构建_分布式Session_79