完成权限校验

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();
    }

}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot拥有强大的权限校验功能,可以使用Interceptor来实现权限校验。 Interceptor是Spring MVC中的一种拦截器,可以在请求进入Controller之前对请求进行拦截和处理。在权限校验方面,Interceptor可以用于拦截请求,并验证用户是否具有执行该请求的权限。 实现Interceptor的步骤如下: 1. 创建一个类,实现HandlerInterceptor接口,该接口定义了Interceptor的三个方法:preHandle、postHandle和afterCompletion。 2. 在preHandle方法中进行权限校验的逻辑处理,可以根据具体的业务需求,判断用户是否具有执行请求的权限。 3. 在Spring Boot的配置类中,通过实现WebMvcConfigurer接口,并重写addInterceptors方法,将Interceptor注册到Spring Boot中。 在Interceptor中进行权限校验的实例代码如下: ```java public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 获取请求的URL路径 String requestUrl = request.getRequestURI(); // 判断请求路径是否需要进行权限校验 if (needAuth(requestUrl)) { // 进行权限校验的逻辑处理 if (hasPermission(request)) { return true; // 具有权限,放行请求 } else { // 没有权限,返回无权限错误 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "You don't have permission to access this resource."); return false; } } else { return true; // 不需要进行权限校验的路径,直接放行请求 } } private boolean needAuth(String requestUrl) { // 判断请求路径是否需要进行权限校验 // 根据业务需求进行判断,返回true表示需要进行权限校验,返回false表示不需要进行权限校验 // 这里只是一个示例,具体判断逻辑需要根据实际需求来实现 return requestUrl.contains("/admin/") || requestUrl.contains("/user/"); } private boolean hasPermission(HttpServletRequest request) { // 判断用户是否具有执行请求的权限 // 根据具体业务需求进行判断,返回true表示用户具有权限,返回false表示用户没有权限 // 这里只是一个示例,具体判断逻辑需要根据实际需求来实现 String token = request.getHeader("Authorization"); // 验证token是否有效 // ... // 这里只是一个示例,具体验证逻辑需要根据实际需求来实现 return token != null && !token.isEmpty(); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 在请求完成后进行处理 } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在视图渲染之后进行处理 } } ``` 然后在Spring Boot的配置类中注册Interceptor: ```java @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private AuthInterceptor authInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/**") .excludePathPatterns("/login"); // 排除登录接口,不进行权限校验 } } ``` 以上代码就实现了基于Interceptor的权限校验功能。需要进行权限校验的请求会被拦截,进行权限校验的逻辑处理,没有权限的请求会返回无权限错误。不需要进行权限校验的请求直接放行。当然,具体的权限校验逻辑需要根据实际业务需求来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值