SSM分布式项目之淘淘商城-第十一天(IDEA)
文章大纲一、第十一天课程计划二、校验数据接口开发三、注册接口开发四、登录接口开发五、token的应用六、登录注册功能实现七、门户首页展示用户名(Jsonp)八、参考博客淘淘商城课程大纲课程大纲一共14天课程(1)第一天:电商介绍–互联网术语-SOA-分布式-集群介绍-环境配置-框架搭建(2)第二天:Dubbo介绍_dubbo框架整合_商品列表查询实现_分页_逆向工程(3)第三天:Git&.
文章大纲
一、第十一天课程计划
二、校验数据接口开发
三、注册接口开发
四、登录接口开发
五、token的应用
六、登录注册功能实现
七、门户首页展示用户名(Jsonp)
八、参考博客
淘淘商城课程大纲
课程大纲
一共14天课程
(1)第一天:电商介绍–互联网术语-SOA-分布式-集群介绍-环境配置-框架搭建
(2)第二天:Dubbo介绍_dubbo框架整合_商品列表查询实现_分页_逆向工程
(3)第三天:Git&.Nginx,类目选择,新增商品
(4)第四天:门户网站介绍&商城首页搭建&内容系统创建&CMS实现
(5)第五天:首页轮播图显示实现,Redis环境搭建,Redis实现缓存
(6)第六天:solr索引库搭建&solr搜索功能实现&图片显示问题解决
(7)第七天:solr集群搭建_全局异常处理
(8)第八天:activeMQ介绍_搭建_解决同步索引库问题
(9)第九天:FreeMark入门_静态化页面标签介绍_静态化页面实现
(10)第十天:Nginx代理详解…单点登录系统工程搭建_接口文档讲解
(11)第十一天:SSO系统的搭建&单点登录系统实现_用户名回显_cookie跨域问题
(12)第十二天:购物车订单系统的实现。
(13)第十三天:订单提交的功能实现&项目的部署&服务器的域名规划。
(14)项目总结。
1. 课程计划
1、SSO注册功能实现
2、SSO登录功能实现
3、通过token获得用户信息
4、ajax跨域请求解决方案–jsonp
2. 校验数据接口开发
检查数据是否可用作为注册功能的辅助。
2.1 功能分析
请求的url:/user/check/{param}/{type}
参数:从url中取参数
1、String param(要校验的数据)
2、Integer type(校验的数据类型)
响应的数据:json数据。TaotaoResult,封装的数据校验的结果为true:表示成功,数据可用,false:失败,数据不可用。
业务逻辑:
1、从tb_user表中查询数据。
2、查询条件根据传递过来的参数动态生成。
3、判断查询结果,如果查询到数据就返回false。
4、如果没有查询到数据就返回true。
5、使用TaotaoResult包装,并返回。
2.2 Dao
从tb_user表查询。属于单表查询,可以使用逆向工程生成的代码。
2.3 Service
先在taotao-sso-interface中定义接口UserRegisterService,
再在taotao-sso-service中写实现类。
参数:
1、要校验的数据:String param
2、数据类型:Integer type(1、2、3分别代表username、phone、email)
返回值:TaotaoResult
在taotao-sso-interface创建接口
public interface UserRegisterService {
/**
* 根据参数和类型来校验数据
* @param param 要校验的数据
* @param type 1 2 3 分别代表 username,phone,email
* @return
*/
public TaotaoResult checkData(String param, Integer type);
}
在taotao-sso-service创建实现类
@Autowired
private TbUserMapper usermapper;
@Override
public TaotaoResult checkData(String param, Integer type) {
//1.注入mapper
//2.根据参数动态的生成查询的条件
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
if(type==1){//username
if(StringUtils.isEmpty(param)){
return TaotaoResult.ok(false);
}
criteria.andUsernameEqualTo(param);
}else if(type==2){
//phone
criteria.andPhoneEqualTo(param);
}else if(type==3){
//email
criteria.andEmailEqualTo(param);
}else{
//是非法的参数
//return 非法的
return TaotaoResult.build(400, "非法的参数");
}
//3.调用mapper的查询方法 获取数据
List<TbUser> list = usermapper.selectByExample(example);
//4.如果查询到了数据 --数据不可以用 false
if(list!=null && list.size()>0){
return TaotaoResult.ok(false);
}
//5.如果没查到数据 ---数据是可以用 true
return TaotaoResult.ok(true);
}
发布服务
先在taotao-sso-service工程中的pom.xml文件中配置对taotao-sso-interface的依赖,因为服务层发布服务要通过该接口,
再在taotao-sso-service工程中的applicationContext-service.xml文件中发布服务:
引用服务
需要在taotao-sso-web中实现。
先在taotao-sso-web工程中的pom.xml文件中配置对taotao-sso-interface的依赖,表现层调用服务要通过该接口,
在taotao-sso-web工程中的springmvc.xml文件中引用服务:
2.4 Controller
请求的url:/user/check/{param}/{type}
参数:从url中取参数
1、String param(要校验的数据)
2、Integer type(校验的数据类型)
请求的方法:get。
响应的数据:json数据。TaotaoResult,封装的数据校验的结果,true:成功;false:失败。
@Autowired
private UserRegisterService registerservice;
/**
* url:/user/check/{param}/{type}
*
* @param param
* @param type 1 2 3
* @return
*/
@RequestMapping(value="/user/check/{param}/{type}",method=RequestMethod.GET)
@ResponseBody
public TaotaoResult checkData(@PathVariable String param,@PathVariable Integer type){
//1.引入服务
//2.注入
//3.调用
return registerservice.checkData(param, type);
}
2.5 测试
启动taotao-sso,taotao-sso-web
双击打开,用这个测试,选择get,输入http://localhost:8088/user/check/zhangsan/1
或者直接在浏览器中输入URL
访问地址:http://localhost:8088/user/check/zhangsan/1
2.6 插曲
我在做这个的时候,刚开始一直出现的是404页面
网上找了很多方法,比如拦截形式改为/,还是没有解决,停滞了将近一天,对比源码也没发现什么错误。。。当时以为没什么,可能是缓存吧,然后接着写登录注册功能,测试的时候发现全部是404。。最后我把这两个工程全部删了,重新创建,重新写(头秃啊),然后就可以了。。。真的是抓狂,这种是真的难受!!!!
3. 注册接口开发
3.1 功能分析
请求的url:/user/register
参数:表单的数据:username、password、phone、email
返回值:json数据。TaotaoResult。
接收参数:使用TbUser对象接收。
请求的方法:post
业务逻辑:
1、使用TbUser接收提交的请求。
2、补全TbUser其他属性。
3、密码要进行MD5加密。
4、把用户信息插入到数据库中。
5、返回TaotaoResult.ok()。
3.2 Dao
可以使用逆向工程生成的代码。
3.3 Service
在taotao-sso-interface中定义接口UserRegisterService已经定义好了,我们直接添加方法即可。
再在taotao-sso-service中写实现类,我们已经定义好了,直接实现方法即可。
参数:TbUser
返回值:TaotaoResult
在taotao-sso-interface创建接口
/**
* 注册用户
* @param user
* @return
*/
public TaotaoResult register(TbUser user);
在taotao-sso-service创建实现类
@Override
public TaotaoResult register(TbUser user) {
//1.注入mapper
//2.校验数据
//2.1 校验用户名和密码不能为空
if(StringUtils.isEmpty(user.getUsername())){
return TaotaoResult.build(400, "注册失败. 请校验数据后请再提交数据");
}
if(StringUtils.isEmpty(user.getPassword())){
return TaotaoResult.build(400, "注册失败. 请校验数据后请再提交数据");
}
//2.2 校验用户名是否被注册了
TaotaoResult result = checkData(user.getUsername(),1);
if(!(boolean)result.getData()){
//数据不可用
return TaotaoResult.build(400, "注册失败. 请校验数据后请再提交数据");
}
//2.3 校验电话号码是否被注册了
if(StringUtils.isNotBlank(user.getPhone())){
TaotaoResult result2 = checkData(user.getPhone(),2);
if(!(boolean)result2.getData()){
//数据不可用
return TaotaoResult.build(400, "注册失败. 请校验数据后请再提交数据");
}
}
//2.4 校验email是否被注册了
if(StringUtils.isNotBlank(user.getEmail())){
TaotaoResult result2 = checkData(user.getEmail(),3);
if(!(boolean)result2.getData()){
//数据不可用
return TaotaoResult.build(400, "注册失败. 请校验数据后请再提交数据");
}
}
//3.如果校验成功 补全其他的属性
user.setCreated(new Date());
user.setUpdated(user.getCreated());
//4.对密码进行MD5加密
String md5password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
user.setPassword(md5password);
//5.插入数据
usermapper.insertSelective(user);
//6.返回taotaoresult
return TaotaoResult.ok();
}
3.4 Controller
请求的url:/user/register
参数:表单的数据:username、password、phone、email
返回值:json数据。TaotaoResult。
接收参数:使用TbUser对象接收。
请求的方法:post
/**
* 注册用户
* @param user
* @return
*/
@RequestMapping(value="/user/register",method=RequestMethod.POST)
@ResponseBody
public TaotaoResult register(TbUser user){
//
TaotaoResult result = registerservice.register(user);
return result;
}
3.5 测试
使用restclient-ui-3.5-jar-with-dependencies.jar测试接口。
Body选择String body
点击编辑按钮,填写表单提交的content-type:application/x-www-form-urlencoded
点击插入参数按钮,填写参数后,点击生成。
点击提交按钮,测试开始。
zhangsan存在了注册不了,换成zhangsansan
4. 登录接口开发
传统登录
分布式登录
4.1 功能分析
请求的url:/user/login
请求的方法:POST
参数:username、password,表单提交的数据。可以使用方法的形参接收。
返回值:json数据,使用TaotaoResult包含一个token。
业务逻辑:
登录的业务流程:
登录的处理流程:
1、登录页面提交用户名密码。
2、登录成功后生成token。token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。key就是token,value就是TbUser对象转换成的json字符串。
4、使用String类型保存session信息。可以使用“前缀:token”为key。
5、设置key的过期时间。模拟session的过期时间。一般半个小时。
6、把token写入cookie中。
7、cookie需要跨域。例如:www.taotao.com\sso.taotao.com\order.taotao.com,可以使用工具类。
8、cookie的有效期。关闭浏览器失效。
9、登录成功。
4.2 Dao
查询tb_user表。单表查询。可以使用逆向工程生成的代码。
4.3 Service
参数:
1、用户名:String username
2、密码:String password
返回值:TaotaoResult,包装token。
业务逻辑:
1、判断用户名和密码是否正确。
2、登录成功后生成token。token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。key就是token,value就是TbUser对象转换成的json串。
4、使用String类型保存session信息。可以使用“前缀:token”为key。
5、设置key的过期时间。模拟session的过期时间。一般半个小时。
6、返回TaotaoResult包装token。
部分代码:需要设置加载属性文件。
在taotao-sso-interface创建接口
/**
* @Description 用户登录接口
* @Author LH
* @Date 2020/9/28 11:47
**/
public interface UserLoginService {
/**
* 根据用户名和密码登录
* @param username
* @param password
* @return
* taotaoresult 登录成功 返回200 并且包含一个token数据
*登录失败:返回400
*/
public TaotaoResult login(String username, String password);
}
在taotao-sso-service创建实现类
@Autowired
private TbUserMapper usermapper;
@Autowired
private JedisClient client;
@Value("${USER_INFO}")
private String USER_INFO;
@Value("${EXPIRE_TIME}")
private Integer EXPIRE_TIME;
@Override
public TaotaoResult login(String username, String password) {
//1.注入mapper
//2.校验用户名和密码是否为空
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
return TaotaoResult.build(400, "用户名或密码错误");
}
//3.先校验用户名
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(username);
List<TbUser> list = usermapper.selectByExample(example);//select* from tbuser where username=123
if(list==null && list.size()==0){
return TaotaoResult.build(400, "用户名或密码错误");
}
//4.再校验密码
TbUser user = list.get(0);
//先加密密码再比较
String md5DigestAsHex = DigestUtils.md5DigestAsHex(password.getBytes());
if(!md5DigestAsHex.equals(user.getPassword())){//表示用户的密码不正确
return TaotaoResult.build(400, "用户名或密码错误");
}
//5.如果校验成功
//6.生成token : uuid生成 ,还需要设置token的有效性期来模拟session 用户的数据存放在redis (key:token,value:用户的数据JSON)
String token = UUID.randomUUID().toString();
//存放用户数据到redis中,使用jedis的客户端,为了管理方便加一个前缀"kkk:token"
//设置密码为空
user.setPassword(null);
client.set(USER_INFO+":"+token, JsonUtils.objectToJson(user));
//设置过期时间 来模拟session
client.expire(USER_INFO+":"+token, EXPIRE_TIME);
//7.把token设置cookie当中 在表现层设置
return TaotaoResult.ok(token);
}
发布服务
在taotao-sso-service工程中applicationContext-service.xml文件中发布服务:
引用服务
在taotao-sso-web工程中的springmvc.xml文件中引用服务:
4.4 Controller
请求的url:/user/login
请求的方法:POST
参数:username、password,表单提交的数据。
因为只有两个参数,所以不用使用pojo来接收,可以使用方法的形参接收就可以。
从返回结果中取出token,写入cookie。需要使用HttpServletRequest、HttpServletResponse
返回值:json数据,使用TaotaoResult包含一个token。
业务逻辑:
1、接收表单提交的两个参数。
2、调用Service层的方法进行登录。
3、从返回结果中取token,写入cookie。注意:cookie要跨域。
cookie的二级域名跨域需要进行设置:
1)setDomain(),设置一级域名:
.itcast.cn
.taotao.com
.taotao.com.cn
2)setPath(),设置为“/”
可以使用工具类。工具类放到taotao-common工程中。
需要加入servlet-api的依赖包:在common项目中的pom.xml中,加入依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
注意:scope的值是provided,表示该jar包在运行时使用、编译时使用、测试时用,但是打包的时候不用,因为该jar包web容器tomcat会提供,如果打包的时候使用该jar包,会出现冲突。
响应数据。json数据。TaotaoResult,其中包含token。
@Autowired
private UserLoginService loginservice;
@Value("${TT_TOKEN_KEY}")
private String TT_TOKEN_KEY;
/**
* url:/user/login
* 参数:username password
* 返回值:json
* 请求限定的方法:post
*/
@RequestMapping(value="/user/login",method=RequestMethod.POST)
@ResponseBody
public TaotaoResult login(HttpServletRequest request,HttpServletResponse response,String username,String password){
//1.引入服务
//2.注入服务
//3.调用服务
TaotaoResult result = loginservice.login(username, password);
//4.需要设置token到cookie中 可以使用 工具类 cookie需要跨域
if(result.getStatus()==200){
CookieUtils.setCookie(request, response,TT_TOKEN_KEY, result.getData().toString());
}
return result;
}
4.5 测试
使用restclient-ui-3.5-jar-with-dependencies.jar测试接口,查看Body;用zhangsansan登录。
然后查看redis服务器中的key
5. token的应用
5.1 功能分析
请求的url:/user/token/{token}
参数:String token(需要从url中取)
返回值:json数据。使用TaotaoResult包装Tbuser对象。
业务逻辑:
1、从url中取参数。
2、根据token查询redis。
3、如果查询不到数据,则返回用户已经过期。
4、如果查询到数据,则说明用户已经登录。
5、需要重置key的过期时间。
6、把json数据转换成TbUser对象,然后使用TaotaoResult包装并返回。
5.2 Dao
使用JedisClient对象进行查询。
5.3 Service
参数:String token
返回值:TaotaoResult
在taotao-sso-interface创建接口
/**
* 根据token获取用户的信息
* @param token
* @return TaotaoResult 应该包含用户的信息
*/
public TaotaoResult getUserByToken(String token);
在taotao-sso-service创建实现类
@Override
public TaotaoResult getUserByToken(String token) {
//1.注入jedisclient
//2.调用根据token查询 用户信息(JSON)的方法 get方法
String strjson = client.get(USER_INFO+":"+token);
//3.判断是否查询到
if(StringUtils.isNotBlank(strjson)){
//5.如果查询到 需要返回200 包含用户的信息 用户信息转成对象
TbUser user = JsonUtils.jsonToPojo(strjson, TbUser.class);
//重新设置过期时间
client.expire(USER_INFO+":"+token, EXPIRE_TIME);
return TaotaoResult.ok(user);
}
//4.如果查询不到 返回400
return TaotaoResult.build(400, "用户已过期");
}
注意:如果返回的是:return TaotaoResult.ok(json);
那么返回的json串中会有转义字符,如下:
{
"status": 200
"msg": "OK"
"data": "{\"id\":1,\"username\":\"zhangsan\",\"password\":null,\"phone\":\"15800807944\",\"email\":\"420840806@qq.com\",\"created\":1414119176000,\"updated\":1414119179000}"
}
这跟我们接口开发文档中的格式不一样,接口开发文档中的格式如下:
{
"status": 200
"msg": "OK"
"data": {"id":1,"username":"zhangzhijun","password":null,"phone":"15800807944","email":"420840806@qq.com","created":1414119176000,"updated":1414119179000}
}
5.4 Controller
请求的url:/user/token/{token}
参数:String token (需要从url中取)
返回值:json数据。使用TaotaoResult包装Tbuser对象。
@RequestMapping(value="/user/token/{token}",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String getUserByToken(@PathVariable String token,String callback){
//判断是否是Jsonp请求
if(StringUtils.isNotBlank(callback)){
//如果是jsonp 需要拼接 类似于fun({id:1});
TaotaoResult result = loginservice.getUserByToken(token);
String jsonpstr = callback+"("+JsonUtils.objectToJson(result)+")";
return jsonpstr;
}
//如果不是jsonp
//1.调用服务
TaotaoResult result = loginservice.getUserByToken(token);
return JsonUtils.objectToJson(result);
}
5.5 测试
使用redis的token
切换get请求
或直接在浏览器中输入URL即可。
6、登录注册功能实现
6.1 展示登录及注册页面及首页超链接修改
第一步:加入静态文件及JSP页面
第二步:在taotao-sso-web的springmvc.xml文件中配置资源映射标签,即不拦截静态资源。
第三步:点击门户首页登录按钮,浏览器URL为:
http://localhost:8084/page/login,报错:
第四步:点击门户首页的免费注册按钮,浏览器的URL为:
http://localhost:8084/page/register,报错:
由此我们发现,其实登录注册的URL非常类似:
http://localhost:8084/page/register
http://localhost:8084/page/login
所以通过/page/{page}的方式请求,通过URL模板映射,使用@PathVariable注解即可接收请求,转发jsp页面。。
编写PageController
/**
* @Description 跳转页面使用的controller
* @Author LH
* @Date 2020/9/28 12:14
**/
@Controller
public class PageController {
@RequestMapping("/page/{page}")
public String showPage(@PathVariable String page){
return page;
}
}
第五步:修改端口号为访问登录注册的端口号为8088
一定要注意修改的是taotao-portal-web中的base-v1.js文件,不要像我修改的是taotao-sso-web的js文件,最后咋搞都搞不出来。。。。。
6.2 测试
注册功能实现
/taotao-sso-web/src/main/webapp/WEB-INF/jsp/register.jsp分析:
提交之前检查:
注册,提交表单:
分析得出,此时的登录功能应该是可以使用了。
登录功能实现
/taotao-sso-web/src/main/webapp/WEB-INF/jsp/login.jsp分析:
文档加载时,调用方法,一旦点击,则提交表单:
表单提交:
分析得出,登录功能也是可以使用的。
7. 门户首页展示用户名
7.1 首页展示用户名分析
1、在taotao-sso-web工程中,当用户登录成功后,在cookie中有token信息。
2、在taotao-portal-web工程中,从cookie中取出token,携带token发送格式为jsonp的请求,根据token从taotao-sso-web工程中查询用户信息。
3、把从taotao-sso-web工程中查询到的用户名展示到首页taotao-portal-web工程中。
方案一:在Controller中取cookie中的token数据,调用sso服务查询用户信息。
缺点:由于淘淘商城首页footer.jsp,在每个系统中都有,可以在每一个系统的footer.jsp中写一个ajax发起请求调用当前系统的Controller,设置模型数据,然后展示数据。麻烦!
方案二:当页面加载完成后使用js取cookie中token的数据,使用ajax请求查询用户信息的JSON数据。只需要在页面实现一次即可。
乍一看方案一与方案二是一样的,其实不是,方案一需要在每一个系统都编写Controller,方案二只需要在taotao-sso-web编写一个Controller即可。而且方案一是立即可行的,但是方案二的服务接口在sso系统中。sso.taotao.com(localhost:8088),在首页显示用户名称,首页的域名是www.taotao.com(localhost:8082),使用ajax请求跨域了。
什么是跨域:
1、域名不同。
2、域名相同端口不同。
例如:
www.taotao.com --> 请求 www.taobao.com 也是跨域
www.taotao.com --> 请求 sso.taotao.com 也是跨域
www.taotao.com:8080 --> www.taotao.com:8088 也是跨域
localhost:8080 --> localhost:8088 也是跨域
www.taotao.com --> www.taotao.com 不是跨域
只有域名和端口完全一样才不是跨域。
js不可以跨域请求JSON数据。
解决js的跨域问题可以使用jsonp。
jsonp不是新技术是跨域的解决方案。使用js的特性绕过跨域请求,特性:js可以跨域加载js文件。
7.2 jsonp原理
举个非常常见的例子,我们在html头部一般都会引入很多js,甚至我们直接引用在线的js,比如我们引用官方网站的jQuery路径加载进来也是可以的。jQuery的官方域名与我们的工程所在的域名肯定是不一样的,但是不影响使用,这就是我们所说的js可以跨域请求js文件!
即:ajax无法跨域请求别的url,我们可以使用ajax跨域加载js文件。
http://localhost:8088/user/token/4ffd07a2-1f92-4601-94ff-2ab763931018?callback=fun
8088做处理:查询到JSON数据,拼接成fun({“id”:1});
8082浏览器加载fun({“id”:1});
调用fun方法,里面参数就是JSON数据。做相关的处理。
7.3 jsonp实现在工程中
修改taotao-sso-web中的taotao.js
更改UserLoginController的getUserByToken方法
@RequestMapping(value="/user/token/{token}",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String getUserByToken(@PathVariable String token,String callback){
//判断是否是Jsonp请求
if(StringUtils.isNotBlank(callback)){
//如果是jsonp 需要拼接 类似于fun({id:1});
TaotaoResult result = loginservice.getUserByToken(token);
String jsonpstr = callback+"("+JsonUtils.objectToJson(result)+")";
return jsonpstr;
}
//如果不是jsonp
//1.调用服务
TaotaoResult result = loginservice.getUserByToken(token);
return JsonUtils.objectToJson(result);
}
7.4 测试效果
8. 参考博客
day78_淘淘商城项目_11_实现SSO系统的登录注册功能 + 门户首页展示用户名 + ajax请求跨域问题详解_匠心笔记
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)