目录
商城业务-认证服务-自定义SpringSession完成子域session共享
商城业务-认证服务-环境搭建
1.创建认证服务
①使用spring初始化向导创建服务

②选择SpringBoot的版本,添加服务需要的依赖
需要注册中心和配置中心的依赖,因此,导入common,还需排除mybatis的依赖,因为不用数据库

2. 开启服务注册功能
①配置配置文件

②开启服务注册功能

Nginx动静分类
①配置域名

②templates中导入登录、注册的html


将注册页面的index.html改名为reg.html,登录页面的html暂时不改名方便后面测试能否获取到Ngixn的静态资源,因为SpringMVC默认访问templates下的index.html,否则需要Controller路由

③上传静态资源至Nginx



④修改访问路径(CTRL+R)


⑤配置网关

商城业务-认证服务-好玩的验证码倒计时
1.编写路径跳转的Controller


如果编写一个接口仅仅是为了跳转页面,没有数据的处理,如果这样的跳转接口多了则可以使用SpringMVC的view Controller(视图控制器)将请求与页面进行绑定

2.为了页面修改能实时看到效果,关闭thymeleaf的缓存

3. 修改首页、注册页、登录页的正确跳转
①点击谷粒商城正确跳转首页


②点击立即注册跳转注册页面


③点击登录跳转登录页面、点击注册跳转注册页面


④点击请登录跳转登录页面


⑤点击谷粒商城跳转首页页面


4.倒计时60s的编写

①为sendCode绑定点击事件

出现问题:可以多次点击,多个事件会叠加在一起
解决方案: 当被点击之后为class值添加disabled,判断class的值是否可点击

商城业务-认证服务-整合短信验证码
1.进入阿里云官网,点击云市场中的三网短信接口

2.购买0元体验
点击官方106三网短信


3. 在管理控制台中查看已购买的短信验证API
有各个参数的详细解释以及java的示例代码

点击短信接口超链接,有详细的API接口文档

Host的获取即调用地址去除/sms/smsSend和(s)


AppCode的获取

AppCode的拼接方式


smsSignId公司的签名,这个需要自己跟客服去申请,就下图圈起来的东西

可以点击去调试按钮进行一个简单的使用体验

4.简单测试
输入你的appCode,HttpUtils按照注释自行下载

将复制的HttpUtils放在第三方服务的util包下,文件上传、短信验证、物流查询都属于第三方服务

5. 编写可配置的发送短信的接口



商城业务-认证服务-验证码防刷校验
1.编写短信验证controller,方便其它服务调用

2. 认证服务远程调用发送短信验证码功能
①依赖已导入,开启远程服务调用功能

② 远程调用接口编写

3.认证服务中编写获取短信验证码的controller

4. 注册页面编写请求发送验证码功能
①为手机号码input框设置id,方便获取

②发送请求,请求后台发送短信验证码

问题1:有人拿到请求路径恶意请求
解决方案:后续会说明
5.防止一个手机号码60s内多次获取短信验证码
解决思路:将短信验证码存储在redis中,key为phoneNum,value为验证码和存储时系统的当前时间。从redis中查询为null则调用发送短信验证码,若查询不为空则判断是否超过60s,是则再次调用发送短信验证码,否则返回提示信息。
①导入redis的依赖,并配置好redis


②common的constant中编写验证码前置

③编写错误代码

④接口编写

@RestController
public class LoginController {
@Autowired
private SmsSCFeignService smsSCFeignService;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/sms/sendcode")
public R sendCode(@RequestParam("phone") String phone){
// TODO 接口防刷
// 处理一个手机号码60s内多次获取短信验证码问题
String s = stringRedisTemplate.opsForValue().get(SmsConstant.SMS_CODE_CACHE_PREFIX + phone);
if (StringUtils.isNotEmpty(s)){
// 获取存储时的系统时间
Long saveTime = Long.parseLong(s.split("_")[1]);
// 单位为毫秒,因此,60秒要转化为毫秒即60000
if (saveTime - System.currentTimeMillis()< 60000){
return R.error(BizCodeEnum.SMS_CODE_EXPTION.getCode(), BizCodeEnum.SMS_CODE_EXPTION.getMsg());
}
}
String code = UUID.randomUUID().toString().substring(0,5);
stringRedisTemplate.opsForValue().set(SmsConstant.SMS_CODE_CACHE_PREFIX+phone,code+"_"+System.currentTimeMillis(),10, TimeUnit.MINUTES);
smsSCFeignService.sendCode(phone,code);
return R.ok();
}
}
⑤ 注册页面的请求发送验证码的回调函数编写

商城业务-认证服务-一步一坑的注册页环境
1.编写注册接口

2.编写Vo封装注册数据

3. 使用JSR303进行数据校验
导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
说明:不导入此依赖,@Valid注解不生效

使用@Valid注解开启数据校验功能,将校验后的结果封装到BindingResult中

4.编写注册页面
为每个input框设置name属性,值需要与Vo的属性名一一对应

点击注册按钮没有发送请求,说明:为注册按钮绑定了单击事件,禁止了默认行为。将绑定的单击事件注释掉

5.为Model绑定校验错误信息
方法一:

方法二:

编写前端页面获取错误信息
①导入thymeleaf的名称空间
![]()
② 封装错误信息


出现问题: 转发失败,打印字符串
出现问题的原因:controller层使用了@RestController注解会自动返回json数据
解决方案:使用@controller注解

出现问题: Request method 'POST' not supported
出现问题的原因:表单的提交使用的是post请求,会原封不动的转发给reg.html,但是/reg.html(路径映射默认都是get方式访问)
解决方案:如下图所示

出现问题:刷新页面,会重复提交表单

出现问题的原因:转发,原封不对转发过去
解决方案:使用重定向

出现问题:转发,数据都封装在Model中,而重定向获取不到
解决方案:使用 RedirectAttributes
RedirectAttributes的方法讲解:Spring MVC ---- RedirectAttributes 使用,请求转发携带参数总结_zhangjian15的博客-优快云博客

出现问题:重定向到服务端口地址

解决方案: 写完整的域名路径

说明: RedirectAttributes的addFlashAttribute()方法是将errors保存在session中,刷新一次就没了

出现问题:分布式下重定向使用session存储数据会出现一些问题
解决方案:后续会说明
商城业务-认证服务-异常机制
1.校验验证码

2. 会员服务中编写Vo接受数据

3. 编写会员服务的用户注册接口

① 查询会员你得默认等级


② 检查用户名和手机号是否唯一
这里采用异常机制处理,如果查出用户名或密码不唯一则向上抛出异常
异常类的编写

检查用户名和手机号是否存在的方法编写


如果抛出异常,则进行捕获

密码的设置,前端传来的密码是明文,存储到数据库中需要进行加密,后面会说到
商城业务-认证服务-MD5&盐值&BCrypt
首先,加密分为可逆加密和不可逆加密。密码的加密为不可逆加密
什么是MD5?

Apache.common下DigestUtils工具类的md5Hex()方法,将MD5加密后的数据转化为16进制

MD5并安全,很多在线网站都可以破解MD5,通过使用彩虹表,暴力破解。

因此,需要通过使用MD5+盐值进行加密
盐值:随机生成的数
什么是加盐?

方法1是加默认盐值:$1$xxxxxxxx
方法2是加自定义盐值

缺点:还需额外在数据库中存储盐值
因此,可以使用Spring家的BCryptPasswordEncoder,它的encode()方法使用的就是MD5+盐值进行加密,盐值是随机产生的

通过matches()方法进行密码是否一致

用户注册业务中的密码加密

商城业务-认证服务-注册完成
1.在common的exception包下,编写异常枚举

2. 进行异常的捕获

3. 远程服务接口编写

4. 远程服务调用

5.注册页错误消息提示

商城业务-认证服务-账号密码登录完成
1.编写Vo

2.数据绑定
将ul包在表单里面

3. 编写登录接口
说明:不能加@RequestBody注解,这里是页面直接提交数据,数据类型是map并非json


4.member服务的Vo编写

5. member服务用户校验接口编写

编写异常枚举


6.远程服务接口编写

7.错误信息提示

商城业务-认证服务-OAuth2.0简介

社交登录使用的是Auth2.0,Auth2.0的流程如下图所示:

商城业务-认证服务-weibo登录测试
1.搜索微博开放平台
地址:新浪微博开放平台-首页
找到微链接,点击网站接入

点击立即接入

填写应用名称

点击我的应用

App Key 和 App Secret 是获取 Access token必填的参数

填写授权回调地址和授权失败回调地址


微博Auth2.0文档地址: 授权机制说明 - 微博API

2.将QQ样式修改为微博样式

3.请求用户授权
https://api.weibo.com/oauth2/authorize?client_id=123050457758183&redirect_uri=http://www.example.com/response&response_type=code


授权成功会跳转成功页面并携带一个code码,可以根据这个code码获取Access token
通过code获取Access token

说明:
①code只能使用一次,Access token只能一段时间有效
②uuid唯一,和关联账户绑定

拿到Access token 可以获取微博开放用户的信息 ,例如用户的性别、年龄等
通过微博提供的用户信息查询接口查询用户信息


商城业务-认证服务-社交登录回调
授权认证的时序图

1.修改授权成功回调页

修改超链接中的回调地址

2. 将third-part服务下的utils包下的HttpUtils工具类复制到common中

3.编写controller专门处理社交登录请求

出现问题:微博登录接入出现错误码21322(重定向地址不匹配)
解决方案:微博登录接入出现错误码21322(重定向地址不匹配),其他解决方法_wwang_dev的博客-优快云博客_21322 微博


清除缓存,重新测试即可
商城业务-认证服务-社交登录完成
1. 编写获取Access token的实体类

将获取到的json数据,利用json在线工具平台转化为java对象



getEntity()获取到json数据,将json数据转为字符串,将字符串转为java对象

2. 为member表新增三个字段

增加新的属性


3.会员服务创建接口处理第一次社交登录
将认证服务的SocialUserVo拷贝到member服务的vo包下



商城业务-认证服务-社交登录测试成功
1.远程服务接口编写

2. 拷贝memberEntity的属性

3.远程服务调用

商城业务-认证服务-分布式session不共享不同步问题
登录成功后,NickName的显示

在之前的单体应用中,会将登录成功后的属性保存到session中

Thymeleaf取出session

出现问题:NickName未显示
出现问题的原因:Session不能跨域使用
auth.gulimall域下的session作用域只限于auth.gulimall域,gulimall域是获取不到的,不共享的


session原理:

分布式下session会出现问题如下:
①同一个服务,复制多份,session不同步问题
②不同服务,session不能共享问题

商城业务-认证服务-分布式session解决方案原理
方案一:sessio复制,不采用

方案二:客户端存储,不采用

方案三: 利用hash一致性,进行负载均衡,可以采用但是这里不采用

方案四: 统一存储,这里采用这套方案

SpringSession已经为我们做好了

放大域名 ,SpringSession也为我们做好了


商城业务-认证服务-SpringSession整合



传送门: Spring Session - Spring Boot
SpringSession整合redis的使用步骤:
①整合依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

② boot配置
spring.session.store-type=redis # Session store type
server.servlet.session.timeout= # Session timeout. If a duration suffix is not specified,seconds is used
spring.session.redis.flush-mode=on_save # Sessions flush mode
spring.session.redis.namespace=spring:session # Namespace for keys used to store sessions

③redis连接配置
spring.redis.host=localhost # Redis server host
spring.redis.password= # Login password of the redis server
spring.redis.port=6379 # Redis server port

④Java配置
使用@EnableRedisHttpSession注解开启Spring Session with Redis功能
@EnableRedisHttpSession
public class Config {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}

1.Auth服务按照上列步骤完成Spring Session with Redis的整合
①整合依赖

② boot配置

③redis连接配置之前已经配置过了,略去
④Java配置
测试:看是否将session存入redis中
出现问题:DefaultSerializer requires a Serializable payload but received an object of type [com.atguigu.gulimall.auth.vo.MemberRespVo]
出现的原因: MemberRespVo未实现序列化解接口
解决方案:

保存成功,redis中有数据

2.Product服务按照上列步骤完成Spring Session with Redis的整合
①导入依赖

②boot配置

③之前已配置过redis连接配置
④Java配置

3. 将MemberRespVo复制到commom中,因为,product服务还需要将Session中存储的loginUser反序列化为MemberRespVo对象

将redis中的session删除,因为,session中存储的loginUser类型为 auth服务vo包下的,需要存储common服务vo包下的类型

4. 手动修改session的作用域



商城业务-认证服务-自定义SpringSession完成子域session共享
解决子域session共享问题:
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}


解决使用json序列化方式来序列化对象数据到redis中
传送门:spring-session/SessionConfig.java at 2.4.6 · spring-projects/spring-session · GitHub
@Configuration
public class SessionConfig implements BeanClassLoaderAware {
private ClassLoader loader;
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer(objectMapper());
}
}



@Configuration
public class GulimallSessionConfig {
/**
* 子域问题共享解决
*/
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("gulimall.com");
cookieSerializer.setCookieName("GULIMALLSESSION");
return cookieSerializer;
}
/**
* 使用json序列化方式来序列化对象数据到redis中
*/
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
给product服务和auth服务各配置一份


前端页面修改,需要进行非空判断

将redis和客户端的session进行清空
商城业务-认证服务-SpringSession原理
@EnableRedisHttpSession注解导入了RedisHttpSessionConfiguration.class这个配置类

在 RedisHttpSessionConfiguration.class这个配置类,为容器中注入了一个组件
sessionRepository -> sessionRedisOperations : redis操作session,实现session的增删改查

调用SpringHttpSessionConfiguration中的springSessionRepositoryFilter()方法,获取一个
SessionRepositoryFilter对象,调用doFilterInternal()对原生的request和response对象进行封装即装饰者模式,request对象调用getSession()方法就会调用wrapperRequest对象的getSession()方法




商城业务-认证服务-页面效果完成
通过账号密码登录的用户信息也保存到session中
①编写一个可修改的属性key

② 用户信息也保存到session中


③设置默认的昵称

④ 登录后,首页页面细化

已经登录的话,在进入登录页要实现跳转首页的效果
①自己编写业务逻辑,将自动页面映射注释

②编写接口

商品详情页,用户昵称显示


搜索页,用户昵称显示
①导入依赖

<!--导入Spring Session with redis 依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--导入SpringBoot整合Redis的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
②配置

③ 开启共享session功能

④ 复制配置类

⑤ 前端代码

商城业务-认证服务-单点登录简介
什么是单点登录?只要注册了登录某一个服务就可以自动登录其它所有服务,例如:注册登录了谷粒商城,则可以自动登录在线教育、众筹系统等

商城业务-认证服务-补-框架效果演示
1.码云搜索xxl,下载单点登录的开源框架

2.配置域名
ssoserver.com 作为认证中心的域名,client1.com、client2.com分别作为客户端1和客户端2的域名

3. 修改认证中心和客户端的配置文件




4.进行打包
mvn clean package -Dmaven.skip.test=true

5.启动认证中心和客户端




认证中心访问路径:http://ssoserver.com:8080/xxl-sso-server
客户端1访问路径:http://client1.com:8081/xxl-sso-web-sample-springboot
客户端2访问路径:http://client2.com:8082/xxl-sso-web-sample-springboot
商城业务-认证服务-单点登录流程-1
单点登录的核心逻辑:

单点登录的第一步流程:
1.使用初始化向导创建认证中心和客户端服务




修改客户端服务的端口号

2. 编写接口


配置认证中心的认证地址

当认证中心认证完成之后,让认证中心知道你要跳转回的地址?解决方案就是在请求参数中携带跳转回的地址

编写list页面

3. 编写处理认证的请求

编写login页面

商城业务-认证服务-单点登录流程-2
1.编写一个隐藏的input框,用于存储调回的url

2.导入redis的依赖,配置redis

3. 登录成功保存用户信息并传递token

4. 拿到令牌需要去认证中心查询用户的信息,这里只是简单保存了以下并没有模拟

商城业务-认证服务-单点登录流程-3
1.认证中心编写接口查询用户信息

2.去认证中心查询用户信息

3.复制client服务,并修改端口、服务名等信息



4.实现一次登录,处处登录的核心就是认证通过之后给浏览器留下一个痕迹,凡是访ssoserver.com这个域名的都会带上这个痕迹,通过使用cookie实现



1111

被折叠的 条评论
为什么被折叠?



