Zuul的权限校验,第一个是买家访问,第二个是/order/finish,这个接口目前还没有,你可以去补充一下,
/order/finish是将订单置为完成状态,我们来补一下这个接口,买家下单卖家来接单,接单就会把它置成完结
状态,只能卖家来操作,第一步先查询订单,根据orderId先查询订单,查询完了之后要判断一下订单状态,并不是
所有的订单都可以置为完结,如果订单状态没问题,就可以修改订单状态了,状态为完结,修改完之后就返回,逻辑
也不算复杂,因为orderId是主键,如果订单不存在就抛一个异常,如果订单存在就继续往下走,
telnet 10.40.8.152 6379
要判断订单的状态,一定是先下的订单,只有新订单才能变成完结的状态,这个逻辑都是我们自己来定的,如果这个
订单不是新订单的话,那么就报异常,如果订单状态也没有问题的话,就可以修改订单状态为完结了,但是由于我们
返回的是OrderDTO,所以你还要构造这么一个对象,orderDTO里面还需要订单详情,所以我们还要把订单详情给他
查询出来,才能填充到这个对象里面,查询订单详情,通过orderId来查订单详情,返回的肯定是一个list,一条orderId
可以对应多条orderDetail,这里也最好判断一下订单详情是否存在,如果不存在也抛一个异常,叫做订单详情不存在,
只能买家或者卖家访问,那么你肯定要知道,买家有什么特征,卖家有什么特征,依靠这些特征把它们区分开来,这里我们
可以简单地使用Cookie,买家是Cookie里有openId,卖家是Cookie里有Token,并且对应的Redis里面,有值,那接下来问题来了,
之后买家和卖家登陆的时候,他应该访问那些服务呢,有人说那不是user服务吗,我是说第一次请求到哪个服务呢,没错,
他首先访问的是api-gateway,再由gateway转发到user服务去,而我们之前登陆的时候,使用的是user服务,所以我们先要
确定一点,他通过访问api-gateway,是否可以成功登陆,成功登陆了他才会往Cookie里面设置值,不然登陆都没有成功,
那你把cookie里面来判断,压根就获取不到,另外把user服务也给启动了,服务已经启动,user这个是8900,我们先访问user,
看一下登陆行不行,8900,这个是卖家登陆,登陆成功
localhost:8900/login/seller?openid=abcd
我们主要看一下cookie,cookie也写进去了,没问题,那我先把cookie给他删掉,我们再通过api-gateway,gateway是8040,
以后肯定是访问api-gateway的,访问这个接口,所以我们来访问一下
localhost:8040/login/seller?openid=abcd
404,这是由于我们少了一个前缀,应该是user
localhost:8040/user/login/seller?openid=abcd
返回的是成功,但是Cookie里面没有值,这就是重点,你看我再刷新,也没有,可以刷新的,这是什么原因呢,之前有跟大家提过zuul,
我们看敏感头设置
zuul.routes.myProduct.sensitiveHeaders=
当时我们是对product服务来设置的,那现在再设置一下也很简单,无非就是user的也写一份,如果我对所有的服务都设置敏感头呢,
都让他传递cookie,该怎么做呢,你可以这么来写,因为在这上面写是没有提示的,如果要排除所有的敏感头的话,这里有一个全局的
配置
zuul.sensitiveHeaders=
你只要设置这个,注意是在zuul的目录后面,全部服务忽略敏感头,全部服务都可以传递cookie,这样子就很清楚了,我们看到这个
时候cookie就已经被写进去了,这是卖家登陆,我们再来试试买家登陆
localhost:8040/user/login/buyer?openid=abc
看openid也被写进去了,现在已经确保我们可以通过api-gateway,是可以正常登陆的,我们接下去就继续编写这块的逻辑,
来判断一下,从哪里获取呢,从request里面获取
if("/order/finish".equals(request.getRequestURI()))
然后你要判断cookie里面是否有cookieId,从这里面获取的是cookieId,要判断一下要获取的值,是否为空,如果为null,
或者cookie里面为空的话,就说明不存在openid的cookie,这个时候你可以返回他,没有权限,同样的如果是这个/order/finish
url,如果能取到token,那还要干一件事情,对应的redis里面也应该有值,我们可以继续判断,或者这个时候我们取redis的,
先来访问创建登订单这个接口
localhost:8040/order/order/create
还有order服务会调用商品服务,所以把商品服务也给启动一下,我们把order和product都启动了,创建成功其实是不符合我们
的预期的,目前我们还没有登陆,所以你看cookie里面没有东西,现在返回的就是401了,权限不足,那么我们先来登陆一次呢,
http://localhost:8900/login/buyer?openid=abc
买家登陆用的应该是这个,先登陆一次,成功了,你看cookie里面已经有了,
现在再到这个地方创建订单,你会发现还是401
使用POSTMAN它是隔离开来的,特别要注意这一点,所以我们自己要给他写一个Cookie,同样的订单完结,/order/finish,我们继续
对这个进行测试,他不仅仅判断cookie,他还判断了redis,所以我们从浏览器里面拿一下,
localhost:8040/user/login/seller?openid=abcd
http://localhost:8040/order/order/finish
token_UUID0617af7a-c4a5-4c34-bf18-7e4c8c13846f
不同的人访问不同的URL,功能我们是写完了,我们再来看一下代码方面,写到filter里面,这里加if语句来判断,其实这种写法
以后是相当不好维护的,其实判断的权限比较多的话呢,你看你现在是判断一个URL,那如果很多呢,再加上这些权限是耦合进来
判断,那有一天产品经理跟你说,我们对买家放开,不限制了,那你是不是要把这一段给删掉呢,有人会说一开始产品定下来就是这样
子,那以后就不会改了,你信谁都不要信产品的话,产品跟你说以后需求不改呢,这就好比你作为一个女生,男朋友教你去酒店,跟你说
我们只是单纯的看看电视,都是一类的问题,关于这块代码如何写的更优雅一些呢,我来写话肯定这样来写,对买家和卖家分别建一个
Filter,比如我就建一个买家的Filter,不是新建,直接拷贝了一份,是否应该拦截,是否应该拦截我们都是通过URL判断的,如果相等的话
就拦截,否则就不拦截
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//对于买家权限这里是一个地址,如果是多个地址就向这里面去加就行了.
// /order/create 只能买家访问(cookie里有openid)
if("/order/order/create".equals(request.getRequestURI())) return true;
else return false;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
Cookie cookie = CookieUtil.get(request, "openid");
if(cookie ==null||StringUtils.isEmpty(cookie.getValue())){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
那拦截之后要处理的逻辑呢,上面就根据条件是否要来拦截,现在是一个地址
https://blog.csdn.net/q610376681/article/details/94132703
那你如果要做多个地址的话,只需要往数据库里面配一些,下面是拦截之后具体的处理逻辑,这是买家端,同理卖家端
也来写一个,卖家,卖家端是/order/finish这个接口,其实逻辑都没有变化,只不过代码上会改动一些
@Override
public boolean shouldFilter() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
if("/order/order/finish".equals(request.getRequestURI())){
return true;
}
return false;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
// /order/finish 只能卖家访问(cookie里有token,并且对应的redis中有值)
Cookie cookie = CookieUtil.get(request, "token");
if(cookie == null
|| StringUtils.isEmpty(cookie.getValue())
|| StringUtils.isEmpty(stringRedisTemplate.opsForValue()
.get(String.format(RedisConstant.Token_TEMPLATE,cookie.getValue())))){
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
改成这样结构上更加清晰一些,不同的角色你就可以写一个Filter,绝对是不会影响另外一个角色的,有一个地方
想跟大家多提两句,你看这里我只会区分买家和卖家,也就是把身份给他鉴别出来,他到底是谁,到底属于什么样的
角色,大部分会把这些信息储存到数据库里面去,那是不是要去连数据库进行判断呢,希望大家在做的时候,不要这么
去做,那是不是会直接去连数据库,拿到信息去判断呢,这里又回到我们之前说的问题了,边界的问题,你看api-gateway
他做什么事情,你如果让他直接去连user,连用户信息的数据库的话,其实是不合适的,有朋友可能要说,那我能不能调用user
的服务,没错你调用user的服务,是可以的,我觉得也应该去调用user的服务,但是你每次鉴权的时候,都去调user的服务,
user服务又去读取数据库的话,这样子还是对数据库压力挺大的,我建议还是利用Redis,api-gateway还是去读Redis里面
的信息,就可以直接判断用户的权限,当然Redis里面的信息怎么过来呢,可以像我们之前讲的异步扩库存的方式,用户信息
一变动之后,你可以发一个消息过来,网关这边监听这个消息,然后把它记录到Redis里面,然后就行了,前面几节我们添加了
用户服务,通过zuul完成对不同角色,URL的控制,微服务架构下,多个微服务都需要对访问进行鉴权,每个微服务都需要明确
当前访问的用户,及其权限,在Zuul的前置过滤器里,实现相关逻辑,是一个值得考虑的方案,同时在微服务框架中,多个服务的
无状态化,一般会考虑两种技术方案,一种是分布式Session,另外一种是Auth,我们都是采用第一种方案,就是将用户认证的信息,
储存在共享储存中,且通常由用户会话作为key,来实现简单的分布式,哈希映射,当用户访问微服务时,用户数据可以从共享储存中
取,用户登录状态是不透明的,同时也是一个高可用且扩展的解决方案,第二种常用的方法,是OAuth2.0与Spring Security结合,
这里我要强调一个细节,我们添加用户服务的过程中,Utils之类的代码一直在Copy,相同的我们就拷贝过来,我当时说少了一个
基础服务,如果公司是比较大型的项目进行改造的话,基础服务会比较容易,一目了然的被拆出来,因为这部分代码已经有了,
所以比较好分辨,但是如果是一个从头开始的项目,不是很有把握,首先将公用组件放到公用模块里面去,就比如我们现在的user,
product服务,里面都有common模块,有了一定的积累之后,很自然的你就可以把这块代码给剥离出来,作为公共组件,成为一个
公共的一个服务,另外一点,由于在Spring Cloud里面的所有微服务,都是通过Zuul来对外提供统一的入口,这个时候如果公司
有两套系统,一套是传统的项目,另外一套是微服务的项目,只要这两套项目同时运行,Zuul会非常关键
package com.learn.cloud.controller;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.learn.cloud.entity.ResultEnum;
import com.learn.cloud.entity.ResultVO;
import com.learn.cloud.entity.UserInfo;
import com.learn.cloud.service.impl.UserServiceImpl;
import com.learn.cloud.utils.CookieUtil;
import com.learn.cloud.utils.ResultVOUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 前端控制器
* @author Leon.Sun
*/
@Slf4j
@RestController
@RequestMapping("/login")
public class UserInfoController {
private final Logger log = LoggerFactory.getLogger(UserInfoController.class);
@Autowired
private UserServiceImpl UserService;
// 操作redis
@Autowired
private StringRedisTemplate stringRedisTemplate;
// @Autowired
// private RedisTemplate<String, Object> redisTemplate;
/**
* 买家登陆
* @param openid
* @param response
* @return
*/
@GetMapping("/buyer")
public ResultVO LoginByBuyer(@RequestParam("openid") String openid, HttpServletResponse response){
log.info("buyer openid"+openid);
// 1.openid和数据库的匹配
UserInfo userInfo= UserService.selectByOpenId(openid);
System.out.println("1:"+userInfo);
if (userInfo==null){
return ResultVOUtil.error(ResultEnum.OPENID_IS_NOT_EXISTS);
}
// 判断角色 1是买家 2是卖家
if(userInfo.getRole()!=1){
return ResultVOUtil.error(ResultEnum.ROLE_ERROR);
}
// 设置cookie (name value 过期时间单位是s)
CookieUtil.set(response,"openid",openid,7200);
log.info("设置cookie成功");
return ResultVOUtil.success();
}
/**
* 卖家登陆
* @param openid
* @param response
* @return
*/
@GetMapping("/seller")
public ResultVO LoginBySeller(@RequestParam("openid") String openid,HttpServletRequest request, HttpServletResponse response){
log.info("seller openid"+openid);
//生成UUID
String token = UUID.randomUUID().toString();
//判断是否登陆 cookie不为null redis不为null
Cookie cookie= CookieUtil.get(request,"token_UUID");
if (cookie!=null && !StringUtils.isEmpty(stringRedisTemplate.opsForValue().get("token_UUID"+cookie.getValue()))){
String tokenValue = stringRedisTemplate.opsForValue().get("token_UUID"+cookie.getValue());
System.out.println(tokenValue);
//这样就会防止不停的往redis里面set数据
return ResultVOUtil.success();
}
//1.openid和数据库的匹配
UserInfo userInfo= UserService.selectByOpenId(openid);
System.out.println("2:"+userInfo);
if (userInfo==null){
return ResultVOUtil.error(ResultEnum.OPENID_IS_NOT_EXISTS);
}
//2判断角色 1是买家 2是卖家
if(userInfo.getRole()!=2){
return ResultVOUtil.error(ResultEnum.ROLE_ERROR);
}
//设置redis key =uuid value =xzy expire 过期时间
// stringRedisTemplate.opsForValue().set(String.format("token_UUID",token),openid,7200, TimeUnit.SECONDS);
stringRedisTemplate.opsForValue().set("token_UUID"+token, openid,60*60*2,TimeUnit.SECONDS);
// stringRedisTemplate.opsForValue().set("token_UUID"+token,openid+"1122",7200, TimeUnit.SECONDS);
// redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
// redisTemplate.opsForValue().set("token_UUID"+token,openid,7200, TimeUnit.SECONDS);
log.info("设置redis成功");
//设置cookie (token=UUID 过期时间单位是s)
CookieUtil.set(response,"token_UUID",token,7200);
log.info("设置cookie成功");
return ResultVOUtil.success();
}
}
package com.learn.cloud.controller;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.learn.cloud.entity.ResultEnum;
import com.learn.cloud.entity.ResultVO;
import com.learn.cloud.entity.UserInfo;
import com.learn.cloud.service.impl.UserServiceImpl;
import com.learn.cloud.utils.CookieUtil;
import com.learn.cloud.utils.ResultVOUtil;
import lombok.extern.slf4j.Slf4j;
/**
* 前端控制器
* @author Leon.Sun
*/
@Slf4j
@RestController
@RequestMapping("/login")
public class UserInfoController {
private final Logger log = LoggerFactory.getLogger(UserInfoController.class);
@Autowired
private UserServiceImpl UserService;
// 操作redis
@Autowired
private StringRedisTemplate stringRedisTemplate;
// @Autowired
// private RedisTemplate<String, Object> redisTemplate;
/**
* 买家登陆
* @param openid
* @param response
* @return
*/
@GetMapping("/buyer")
public ResultVO LoginByBuyer(@RequestParam("openid") String openid, HttpServletResponse response){
log.info("buyer openid"+openid);
// 1.openid和数据库的匹配
UserInfo userInfo= UserService.selectByOpenId(openid);
System.out.println("1:"+userInfo);
if (userInfo==null){
return ResultVOUtil.error(ResultEnum.OPENID_IS_NOT_EXISTS);
}
// 判断角色 1是买家 2是卖家
if(userInfo.getRole()!=1){
return ResultVOUtil.error(ResultEnum.ROLE_ERROR);
}
// 设置cookie (name value 过期时间单位是s)
CookieUtil.set(response,"openid",openid,7200);
log.info("设置cookie成功");
return ResultVOUtil.success();
}
/**
* 卖家登陆
* @param openid
* @param response
* @return
*/
@GetMapping("/seller")
public ResultVO LoginBySeller(@RequestParam("openid") String openid,HttpServletRequest request, HttpServletResponse response){
log.info("seller openid"+openid);
//生成UUID
String token = UUID.randomUUID().toString();
//判断是否登陆 cookie不为null redis不为null
Cookie cookie= CookieUtil.get(request,"token_UUID");
if (cookie!=null && !StringUtils.isEmpty(stringRedisTemplate.opsForValue().get("token_UUID"+cookie.getValue()))){
String tokenValue = stringRedisTemplate.opsForValue().get("token_UUID"+cookie.getValue());
System.out.println(tokenValue);
//这样就会防止不停的往redis里面set数据
return ResultVOUtil.success();
}
//1.openid和数据库的匹配
UserInfo userInfo= UserService.selectByOpenId(openid);
System.out.println("2:"+userInfo);
if (userInfo==null){
return ResultVOUtil.error(ResultEnum.OPENID_IS_NOT_EXISTS);
}
//2判断角色 1是买家 2是卖家
if(userInfo.getRole()!=2){
return ResultVOUtil.error(ResultEnum.ROLE_ERROR);
}
//设置redis key =uuid value =xzy expire 过期时间
// stringRedisTemplate.opsForValue().set(String.format("token_UUID",token),openid,7200, TimeUnit.SECONDS);
stringRedisTemplate.opsForValue().set("token_UUID"+token, openid,60*60*2,TimeUnit.SECONDS);
// stringRedisTemplate.opsForValue().set("token_UUID"+token,openid+"1122",7200, TimeUnit.SECONDS);
// redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
// redisTemplate.opsForValue().set("token_UUID"+token,openid,7200, TimeUnit.SECONDS);
log.info("设置redis成功");
//设置cookie (token=UUID 过期时间单位是s)
CookieUtil.set(response,"token_UUID",token,7200);
log.info("设置cookie成功");
return ResultVOUtil.success();
}
}