四路:- 这个操作是否应该在网关来做? 服务之间的调用权限还是适合在服务自身处理
a: 创建拦截器1,用于服务之间的请求校验
b: 创建拦截器2,用于restTemplate请求的校验
1- 编写拦截器1
package spring.cloud.common.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.alibaba.fastjson.JSON; import spring.cloud.common.context.UserContextHolder; import spring.cloud.common.util.UserPermissionUtil; import spring.cloud.common.vo.User; public class UserContextInterceptor extends HandlerInterceptorAdapter { private static final Logger log = LoggerFactory.getLogger(UserContextInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse respone, Object arg2) throws Exception { User user = getUser(request); // TODO 日常测试先关闭服务间的资源权限校验 // UserPermissionUtil.permission(user); // if(!UserPermissionUtil.verify(user,request)) { // respone.setHeader("Content-Type", "application/json"); // String jsonstr = JSON.toJSONString("no permisson access service, please check"); // respone.getWriter().write(jsonstr); // respone.getWriter().flush(); // respone.getWriter().close(); // throw new PermissionException("no permisson access service, please check"); // } UserContextHolder.set(user); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse respone, Object arg2, ModelAndView arg3) throws Exception { // DOING NOTHING } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse respone, Object arg2, Exception arg3) throws Exception { UserContextHolder.shutdown(); } private User getUser(HttpServletRequest request){ String userid = request.getHeader("x-user-id"); String username = request.getHeader("x-user-name"); User user = new User(); user.setUserId(userid); user.setUserName(username); return user; } static class PermissionException extends RuntimeException { private static final long serialVersionUID = 1L; public PermissionException(String msg) { super(msg); } } }
2 - 编写校验工具类
package spring.cloud.common.util; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.springframework.util.StringUtils; import spring.cloud.common.vo.User; public class UserPermissionUtil { /** * 模拟权限校验, 可以根据自己项目需要定制不同的策略,如查询数据库获取具体的菜单url或者角色等等. * @param user */ public static boolean verify(User user, HttpServletRequest request){ // TODO 从请求头获取用户信息,再查询DB判断该用户是否具有该服务或者该服务下某个url的权限 String url = request.getHeader("x-user-serviceName"); if(StringUtils.isEmpty(user)) { return false; }else { List<String> str = user.getAllowPermissionService(); for (String permissionService : str) { if(url.equalsIgnoreCase(permissionService)) { return true; } } return false; } } /** * 模拟权限赋值, 可以根据自己项目需要定制不同的策略,如查询数据库获取具体的菜单url或者角色等等. * 该方法应该放到业务层,通过访问DB获取权限资源列表 * @param user */ public static void permission(User user){ if(user.getUserName().equals("admin")) { List allowPermissionService = new ArrayList(); // TODO 查询DB资源权限数据 allowPermissionService.add("client-service"); allowPermissionService.add("provider-service"); allowPermissionService.add("SPRING-CLOUD-FEIGN"); user.setAllowPermissionService(allowPermissionService); }else if(user.getUserName().equals("spring")) { List allowPermissionService = new ArrayList(); allowPermissionService.add("client-service"); user.setAllowPermissionService(allowPermissionService); } else { List allowPermissionService = new ArrayList(); user.setAllowPermissionService(allowPermissionService); } } }
3- 编写上下文持有对象
package spring.cloud.common.context; import spring.cloud.common.vo.User; public class UserContextHolder { public static ThreadLocal<User> context = new ThreadLocal<>(); public static User currentUser() { return context.get(); } public static void set(User user) { context.set(user); } public static void shutdown() { context.remove(); } }
4- 编写拦截器2
package spring.cloud.common.interceptor; import java.io.IOException; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import spring.cloud.common.context.UserContextHolder; import spring.cloud.common.vo.User; public class RestTemplateUserContextInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { User user = UserContextHolder.currentUser(); request.getHeaders().add("x-user-id",user.getUserId()); request.getHeaders().add("x-user-name",user.getUserName()); request.getHeaders().add("x-user-serviceName",request.getURI().getHost()); return execution.execute(request, body); } }
5 将2个拦截器注入IOC容器
package spring.cloud.common.config; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import spring.cloud.common.interceptor.RestTemplateUserContextInterceptor; import spring.cloud.common.interceptor.UserContextInterceptor; /** * 详见 https://www.cnblogs.com/duanxz/p/4875153.html */ @Configuration @EnableWebMvc public class CommonConfiguration implements WebMvcConfigurer { /** * 请求拦截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { System.out.println("--------------UserContextInterceptor-------------"); registry.addInterceptor(new UserContextInterceptor()); //可以指定拦截器的路劲 //registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**"); //registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*"); } /*** * RestTemplate 拦截器,在发送请求前设置鉴权的用户上下文信息 * @return */ @LoadBalanced @Bean public RestTemplate restTemplate() { System.out.println("--------------resttemplate-------------"); RestTemplate restTemplate = new RestTemplate(); restTemplate.getInterceptors().add(new RestTemplateUserContextInterceptor()); return restTemplate; } }