一.课程计划
1 | 1、sso注册功能实现 |
二.Sso系统工程搭建
1 | 需要创建一个sso服务工程,可以参考e3-manager创建。 |
三.服务接口实现
功能分析
- 先看数据库中的User表
- 再看注册页面
- 页面请求详解
3.1 检查数据是否可用
3.1.1 功能分析
请求的url:/user/check/{param}/{type}
参数:从url中取参数1、String param(要校验的数据)2、Integer type(校验的数据类型)
响应的数据:json数据。e3Result,封装的数据校验的结果true:成功false:失败。
业务逻辑:
- 从tb_user表中查询数据
- 查询条件根据参数动态生成。
- 判断查询结果,如果查询到数据返回false。
- 如果没有返回true。
- 使用e3Result包装,并返回。
3.1.2 Dao层
从tb_user表查询。可以使用逆向工程。
3.1.3 Service
参数:
- 要校验的数据:String param
- 数据类型:int type(1、2、3分别代表username、phone、email)
- 返回值:e3Result
接口定义:
实现类:
1 | /** |
发布服务:
1 | <?xml version="1.0" encoding="UTF-8"?> |
3.1.4 表现层
需要在e3-sso-web中实现
引用服务
Controller:
请求的url:/user/check/{param}/{type}
参数:从url中取参数
1、String param(要校验的数据)
2、Integer type(校验的数据类型)
响应的数据:json数据。e3Result,封装的数据校验的结果true:成功false:失败。
1 | @ResponseBody |
3.2 用户注册
3.2.1 功能分析
请求的url:/user/register
参数:表单的数据:username、password、phone、email
返回值:json数据。e3Result
接收参数:使用TbUser对象接收。
请求的方法:post
业务逻辑:
1、使用TbUser接收提交的请求。
2、补全TbUser其他属性。
3、密码要进行MD5加密。
4、把用户信息插入到数据库中。
5、返回e3Result。
3.2.2 Dao层
可以使用逆向工程。
3.2.3 Service层
参数:TbUser
返回值:e3Result
接口
1 | import yp.e3mall.common.utils.E3Result; |
实现类中注册方法
1 | @Override |
spring框架自带MD5加密技术,所以可以直接使用。
3.2.4 表现层
Controller:
请求的url:/user/register
参数:表单的数据:username、password、phone、email
返回值:json数据。e3Result
接收参数:使用TbUser对象接收。
请求的方法:post
1 | @ResponseBody |
3.2.5 测试
3.3 用户登录
3.3.1 功能分析
请求的url:/user/login
请求的方法:POST
参数:username、password,表单提交的数据。可以使用方法的形参接收。
返回值:json数据,使用e3Result包含一个token。
业务逻辑:
登录的业务流程:
登录的处理流程:
- 登录页面提交用户名密码。
- 登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
- 把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
- 使用String类型保存Session信息。可以使用“前缀:token”为key
- 设置key的过期时间。模拟Session的过期时间。一般半个小时。
- 把token写入cookie中。
- Cookie需要跨域。例如www.e3.com\sso.e3.com\order.e3.com,可以使用工具类。
- Cookie的有效期。关闭浏览器失效。
- 登录成功。
3.3.2 Dao层
查询tb_user表。单表查询。可以使用逆向工程。
3.3.3 Service层
参数:
1、用户名:String username
2、密码:String password
返回值:e3Result,包装token。
业务逻辑:
1、判断用户名密码是否正确。
2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
4、使用String类型保存Session信息。可以使用“前缀:token”为key
5、设置key的过期时间。模拟Session的过期时间。一般半个小时。
6、返回e3Result包装token。
接口:
1 | public interface LoginService { |
实现类:
1 | /** |
发布服务:
1 | <dubbo:service interface="yp.e3mall.sso.service.LoginService" ref="loginServiceImpl" |
3.3.4 表现层
引用服务:
Controller
分析:
请求的url:/user/login
请求的方法:POST
参数:username、password,表单提交的数据。可以使用方法的形参接收。
HttpServletRequest、HttpServletResponse
返回值:json数据,使用e3Result包含一个token。
业务逻辑:
- 接收两个参数。
- 调用Service进行登录。
- 从返回结果中取token,写入cookie。Cookie要跨域。
Cookie二级域名跨域需要设置:
①setDomain,设置一级域名:
.itcatst.cn
.e3.com
.e3.com.cn
②setPath。设置为“/”
工具类放到e3-common工程中。
响应数据。Json数据。e3Result,其中包含Token。
1 | /** |
3.4 通过token查询用户信息
3.4.1 功能分析
请求的url:/user/token/{token}
参数:String token需要从url中取。
返回值:json数据。使用e3Result包装Tbuser对象。
业务逻辑:
1、从url中取参数。
2、根据token查询redis。
3、如果查询不到数据。返回用户已经过期。
4、如果查询到数据,说明用户已经登录。
5、需要重置key的过期时间。
6、把json数据转换成TbUser对象,然后使用e3Result包装并返回。
3.4.2 Dao层
使用JedisClient对象。
3.4.3 Service层
参数:String token
返回值:e3Result
接口:1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 根据Token查询用户信息
*/
public interface TokenService {
/**
* 根据token获取用户信息
* @param token
* @return
*/
E3Result getUserByToken(String token);
}
实现类:
1 | /** |
3.4.4 表现层
请求的url:/user/token/{token}
参数:String token需要从url中取。
返回值:json数据。使用e3Result包装Tbuser对象。
1 | /** |
四.登录注册页面整合首页
4.1 首页跳转到登录、注册页面
第一步:把静态页面添加到工程中。
第二步:展示页面。
请求的url:
登录:/page/login
注册:/page/register
参数:无
返回结果:逻辑视图String
4.2 首页展示用户名(可以作为一个难点来说)
1、当用户登录成功后,在cookie中有token信息。
2、从cookie中取token根据token查询用户信息。
3、把用户名展示到首页。
方案一:在Controller中取cookie中的token数据,调用sso服务查询用户信息。
方案二:当页面加载完成后使用js取token的数据,使用ajax请求查询用户信息。
js处理流程:之前我们不是有个接口是 /user/token/{token} 这个,我们的方案是使用第二种,使用js来请求这个接口,这样就可以拿出用户信息了,然后给每个页面引入这个js就可以了。
接下来我们先在首页写这么一个js,测试一下
分析:
> 这是为什么呢???
因为这个ajax请求(就请求token的)是属于 e3_portal_web(localhost:8083) 工程中的,而我们的ajax请求的是 e3_sso_service(localhost:8089) 工程中的,这就牵扯跨域问题了。
我们下面来看一下什么事跨域
Js不可以跨域请求数据。
什么是跨域:
1、域名不同
2、域名相同端口不同。
解决js的跨域问题可以使用jsonp。
Jsonp不是新技术,跨域的解决方案。使用js的特性绕过跨域请求。Js可以跨域加载js文件。
4.3.Jsonp原理
我们可以来分析一下这个图,在传统的Ajax跨域请求的时候,即使我们请求成功了,而且可以在浏览器中看到状态是200,但是就是没有返回的数据,这是为什么呢?可能是出于安全着想,所以浏览器是不会让你访问的。
而我们如何处理呢?
我们可以上面的图,首先我们在写跨域请求的Ajax的js文件中写这样一个js函数mycall(),先不管这是干啥的。然后在我们写ajax请求的时候加上callback这个参数,当我们后台接受到这个请求的时候就去检查它的参数中有没有callback,有就代表这是一个跨域的ajax请求,需要我们后台来配合,于是我们后台返回这样一个数据 mycall({id:1,name:z});。
这其实是利用js可以跨域加载js文件,当 mycall({id:1,name:z}); 这个数据到达浏览器后就触发浏览器中 mycall()的这个方法,从而拿到数据。
后台返回的其实就是个方法调用。
当然这样太麻烦了啊,我们还要自己写这么多的js,而且我不太擅长js,所以人家jquery给我们封装好了,我们只需要将之前我们注释的ajax请求中的dataType的类型改为jsonp就ok了
4.4 Json实现
4.4.1 客户端
使用jQuery。
我们说了jquery帮我们封装好了jsonp
4.4.2 服务端
- 接收callback参数,取回调的js的方法名。
- 业务逻辑处理。
- 响应结果,拼接一个js语句。
Controller
1 | /** |
produces= MediaType.APPLICATION_JSON_UTF8_VALUE,这个是将页面显示数据的格式设置为json,如果不设置我们返回的是一个Sting,它的显示格式是 text/什么(忘了) 显示的大,这样设置就好了。
4.4.3 Spring4之后的新方法
1 | @RequestMapping(value = "/user/token/{token}") |
这里我们使用了一个MappingJacksonValue这个对象,这是spring帮我们封装好的解决jsonp请求的对象
mappingJacksonValue.setJsonpFunction(callback);这是设置js语句