Loading... ## 1、项目架构 ### 1.1 概述 API网关有很多实现方式,我们通过SpringCloud Gateway实现 使用Nacos作为配置中心 ![](https://blog.fivk.cn/usr/uploads/2023/09/3400050683.png) ### 1.2 API网关 ### 1.2.1 搭建网关 #### 依赖 ```xml <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- 监控检查--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- nacos配置中心依赖支持 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> --> <dependency> <groupId>com.itheima</groupId> <artifactId>tanhua-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> ``` #### 引导类 ```java @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } ``` #### 跨域问题配置类 ```java package com.itheima.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.util.pattern.PathPatternParser; /** * 跨域支持 */ @Configuration public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } } ``` #### 配置文件 ```yml server: port: 8888 spring: profiles: active: prod application: name: tanhua-gateway cloud: nacos: discovery: server-addr: 192.168.136.160:8848 gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedHeaders: "*" allowedOrigins: "*" allowedMethods: - GET - POST - DELETE - PUT - OPTION ``` ```yml server: port: 8888 spring: profiles: active: prod application: name: tanhua-gateway cloud: nacos: discovery: server-addr: 192.168.136.160:8848 gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedHeaders: "*" allowedOrigins: "*" allowedMethods: - GET - POST - DELETE - PUT - OPTION routes: # 探花系统 - id: tanhua-app-server uri: lb://tanhua-app-server predicates: - Path=/app/** filters: - StripPrefix= 1 # 后台系统 - id: tanhua-admin uri: lb://tanhua-admin predicates: - Path=/admin/** filters: - StripPrefix= 1 gateway: excludedUrls: /user/login,/user/loginVerification,/system/users/verification,/system/users/login ``` ### 1.2.2 配置鉴权管理器 ```java @Component public class AuthFilter implements GlobalFilter, Ordered { //排除的链接 @Value("#{'${gateway.excludedUrls}'.split(',')}") private List<String> excludedUrls; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String url = exchange.getRequest().getURI().getPath(); System.out.println( "url:"+ url); //排除特殊接口 不校验 if(excludedUrls.contains(url)){ return chain.filter(exchange); } String token = exchange.getRequest().getHeaders().getFirst("Authorization"); if(!StringUtils.isEmpty(token)){ token = token.replace("Bearer ", ""); } ServerHttpResponse response = exchange.getResponse(); //2、使用工具类,判断token是否有效 boolean verifyToken = JwtUtils.verifyToken(token); //3、如果token失效,返回状态码401,拦截 if(!verifyToken) { Map<String, Object> responseData = new HashMap<>(); responseData.put("errCode", 401); responseData.put("errMessage", "用户未登录"); return responseError(response,responseData); } return chain.filter(exchange); } //响应错误数据 private Mono<Void> responseError(ServerHttpResponse response,Map<String, Object> responseData){ // 将信息转换为 JSON ObjectMapper objectMapper = new ObjectMapper(); byte[] data = new byte[0]; try { data = objectMapper.writeValueAsBytes(responseData); } catch (JsonProcessingException e) { e.printStackTrace(); } // 输出错误信息到页面 DataBuffer buffer = response.bufferFactory().wrap(data); response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); return response.writeWith(Mono.just(buffer)); } /** * 设置过滤器的执行顺序 */ @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } } ``` ### 1.3 Nacos配置中心 Nacos提供了注册中心和配置管理的能力,使用Nacos可以快速实现服务发现、服务配置管理等需求 ![](https://blog.fivk.cn/usr/uploads/2023/09/3552173125.png) #### 1.3.1 添加依赖 ```xml <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> ``` #### 1.3.2 添加bootstrap.yml配置 ```yml server: port: 8888 spring: profiles: active: prod application: name: tanhua-gateway cloud: nacos: discovery: server-addr: 192.168.136.160:8848 config: server-addr: 192.168.136.160:8848 file-extension: yml ``` #### 1.3.3 nacos添加配置 ```yml server: port: 9997 spring: cloud: gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedHeaders: "*" allowedOrigins: "*" allowedMethods: - GET - POST - DELETE - PUT - OPTION routes: # 用户微服务 - id: tanhua-app-server uri: lb://tanhua-app-server predicates: - Path=/app/** filters: - StripPrefix= 1 # 文章微服务 - id: tanhua-admin uri: lb://tanhua-admin predicates: - Path=/admin/** filters: - StripPrefix= 1 gateway: excludedUrls: /user/login,/user/loginVerification,/system/users/verification,/system/users/login ``` ## 2、后台系统 ### 2.1 概述 探花交友APP建立的后台管理系统,目的是完成探花交友项目的业务闭环,主要功能包括:用户管理、动态管理、审核管理以及系统管理。 ![](https://blog.fivk.cn/usr/uploads/2023/09/2406613156.png) 课程中实现的功能有:登录、首页、用户管理、动态审核。 ### 2.2 环境前端搭建 #### 2.2.1 导入数据库 将资料中的`tanhua-admin.sql`引入到mysql数据库中 ![](https://blog.fivk.cn/usr/uploads/2023/09/1980017449.png) #### 2.2.2 导入静态页面 后台系统也是采用前后端分离的方式,前端采用Vue.js实现,关于前端系统我们不进行实现,拿来直接使用。 ![](https://blog.fivk.cn/usr/uploads/2023/09/2814971124.png) ##### nginx安装 将资料中提供的nginx解压到合适的位置 ![](https://blog.fivk.cn/usr/uploads/2023/09/1031116642.png) 其中html目录中为,vue编译后的所有页面。 修改Nginx的`/conf/nginx.conf`配置文件: ~~~shell server { listen 8088; #请求端口 server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } location /management { proxy_pass http://127.0.0.1:18083/; #转发后台地址 } #....略 } ~~~ * 访问vue页面的路径:[http://localhost:8088/](http://localhost:8088/) * 其中内部调用java服务器的路径 :[http://127.0.0.1:18083/](http://127.0.0.1:18083/) ##### 测试 双击`nginx.exe`,待启动完成后访问:[http://127.0.0.1:8088](http://127.0.0.1:8088)即可访问后台项目 ### 2.3 搭建后端环境 #### 2.3.1 Admin实体类 ``` //后台系统的管理员对象 @Data @NoArgsConstructor @AllArgsConstructor public class Admin implements Serializable { /** * id */ private Long id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 头像 */ private String avatar; } ``` #### 2.3.2 导入项目 将今日资料中的tanhua-admin模块导入到探花项目中,完成开发。 ![](https://blog.fivk.cn/usr/uploads/2023/09/1046495394.png) #### 2.3.3 nacos中加入配置 DataID:tanhua-admin-prod.yml ```yml spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/tanhua-admin?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false username: root password: root rabbitmq: host: 192.168.136.160 port: 5672 username: guest password: guest redis: host: 192.168.136.160 port: 6379 cloud: #nacos配置 nacos: discovery: server-addr: 192.168.136.160:8848 dubbo: #dubbo配置 registry: address: spring-cloud://localhost consumer: check: false retries: 0 protocols: dubbo: port: -1 #配置短信平台信息 tanhua: #手机验证码,咱们自己注册(不要白嫖) sms: signName: 物流云商 templateCode: SMS_106590012 accessKey: LTAI4GKgob9vZ53k2SZdyAC7 secret: LHLBvXmILRoyw0niRSBuXBZewQ30la oss: accessKey: LTAI4GKgob9vZ53k2SZdyAC7 secret: LHLBvXmILRoyw0niRSBuXBZewQ30la endpoint: oss-cn-beijing.aliyuncs.com bucketName: tanhua143 url: https://tanhua143.oss-cn-beijing.aliyuncs.com aip: appId: 22837663 apiKey: nA43galrxfUZTGtYRVK8F8tb secretKey: MQp567q4nGnIKfniURa2XAw8bT1SlPE3 huanxin: appkey: 1110201018107234#tanhua clientId: YXA6nxJJ_pdEQ_eYUlqcRicS4w clientSecret: YXA6GMUxVEZhAvxlMn4OvHSXbWuEUTE #mybaits-plus mybatis-plus: global-config: db-config: table-prefix: tb_ #数据库表前缀 id-type: auto #数据库表主键的策略 ``` ### 2.4 管理员登录 后台系统的登录模块独立于APP端的登录。 #### 2.4.1 生成验证码 验证码:页面端发送请求到服务端。服务端生成一个验证码的图片,已流的形式返回 ##### SystemController ```java /** * 生成图片验证码 */ @GetMapping("/verification") public void verification(String uuid, HttpServletResponse response) throws IOException { //设置响应参数 response.setDateHeader("Expires", 0); // Set standard HTTP/1.1 no-cache headers. response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); // Set IE extended HTTP/1.1 no-cache headers (use addHeader). response.addHeader("Cache-Control", "post-check=0, pre-check=0"); // Set standard HTTP/1.0 no-cache header. response.setHeader("Pragma", "no-cache"); response.setContentType("image/jpeg"); //1、通过工具类生成验证码对象(图片数据和验证码信息) LineCaptcha captcha = CaptchaUtil.createLineCaptcha(299, 97); String code = captcha.getCode(); //1234 //2、调用service,将验证码存入到redis redisTemplate.opsForValue().set(Constants.CAP_CODE+uuid,code); //3、通过输出流输出验证码 captcha.write(response.getOutputStream()); } ``` #### 2.4.2 用户登录 ##### SystemController ```java /** * 用户登录: */ @PostMapping("/login") public ResponseEntity login(@RequestBody Map map) { Map retMap = adminService.login(map); return ResponseEntity.ok(retMap); } ``` ##### SystemService ```java //用户登录 public ResponseEntity login(Map map) { //1、获取请求的参数(username,password,verificationCode(验证码),uuid) String username = (String) map.get("username"); String password = (String) map.get("password"); String verificationCode = (String) map.get("verificationCode"); String uuid = (String) map.get("uuid"); //2、判断用户名或者密码是否为空 if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { //用户名或者密码为空 //throw new BusinessException("用户名或者密码为空"); Map map1 = new HashMap(); map.put("message","用户名或者密码为空"); return ResponseEntity.status(500).body(map1); } //3、判断验证码是否正确 if (StringUtils.isEmpty(username)) { //验证码为空 throw new BusinessException("验证码为空"); } //从redis中获取验证码 String key = Constants.CAP_CODE+uuid; String code = redisTemplate.opsForValue().get(key); if (StringUtils.isEmpty(code) || !code.equals(verificationCode)) { //验证码错误 throw new BusinessException("验证码错误"); } redisTemplate.delete(key); //4、根据用户名查询用户 QueryWrapper<Admin> qw = new QueryWrapper<>(); qw.eq("username", username); Admin admin = adminMapper.selectOne(qw); //5、判断用户是否存在,密码是否一致 password = SecureUtil.md5(password); //md5加密 if(admin == null || !admin.getPassword().equals(password)) { //用户名错误或者密码不一致 throw new BusinessException("用户名或者密码"); } //6、通过JWT生成token Map<String, Object> claims = new HashMap<String, Object>(); claims.put("username", username); claims.put("id", admin.getId()); String token = JwtUtils.getToken(claims); //8、构造返回值 Map res = new HashMap(); res.put("token", token); return ResponseEntity.ok(res); } ``` #### 2.4.3 获取用户资料 ##### AdminVO ```java @Data @NoArgsConstructor @AllArgsConstructor public class AdminVo { /** * id */ private String id; /** * 用户名 */ private String username; /** * 头像 */ private String avatar; public static AdminVo init(Admin admin) { AdminVo vo = new AdminVo(); vo.setAvatar(admin.getAvatar()); vo.setUsername(admin.getUsername()); vo.setId(admin.getId().toString()); return vo; } } ``` ##### SystemController ```java /** * 获取用户的信息 */ @PostMapping("/profile") public ResponseEntity profile() { AdminVo vo = adminService.profile(); return ResponseEntity.ok(vo); } ``` ##### SystemService ```java //获取当前用户的用户资料 public AdminVo profile() { Long id = AdminHolder.getId(); Admin admin = adminMapper.selectById(id); return AdminVo.init(admin); } ``` ## 3、用户管理 ### 3.1 需求分析 用户管理:对探花交友客户端注册用户的管理(查询业务数据),使用Dubbo的形式调用tanhua-dubbo-service获取响应的数据结果 ### 3.2 查询用户列表 #### ManageController ```java @GetMapping("/users") public ResponseEntity users(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pagesize) { PageResult result = managerService.findAllUsers(page,pagesize); return ResponseEntity.ok(result); } ``` #### ManagerService ```java public ResponseEntity findAllUsers(Integer page, Integer pagesize) { //1、调用API分页查询数据列表 Ipage<UserInfo> IPage<UserInfo> iPage = userInfoApi.findAll(page,pagesize); //2、需要将Ipage转化为PageResult PageResult result = new PageResult(page, pagesize, iPage.getTotal(), iPage.getRecords()); //3、构造返回值 return ResponseEntity.ok(result); } ``` #### UserInfoAPI ```java @Override public IPage<UserInfo> findAll(Integer page, Integer pagesize) { return userInfoMapper.selectPage(new Page<UserInfo>(page,pagesize),null); } ``` ### 3.3 查看用户详情 #### ManageController ```java /** * 根据id查询用户详情 */ @GetMapping("/users/{userId}") public ResponseEntity findById(@PathVariable("userId") Long userId) { return managerService.findById(userId); } ``` #### ManagerService ```java //根据id查询用户详情 public ResponseEntity findById(Long userId) { UserInfo info = userInfoApi.findById(userId); return ResponseEntity.ok(info); } ``` ### 3.4 查看视频列表 #### ManageController ```java /** * 查询指定用户发布的所有视频列表 */ @GetMapping("/videos") public ResponseEntity videos(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pagesize, Long uid ) { PageResult result = managerService.findAllVideos(page,pagesize,uid); return ResponseEntity.ok(result) } ``` #### ManagerService ```java //根据用户id分页查询此用户发布的所有视频列表 public PageResult findAllVideos(Integer page, Integer pagesize, Long userId) { //1、调用API查询视频列表(PageResult<video>) List<Video> items = videoApi.findByUserId(page,pagesize,userId); //2、获取到分页对象中的List List<Video> UserInfo info = userInfoApi.findById(userId); //3、一个Video转化成一个VideoVo List<VideoVo> list = new ArrayList<>(); for (Video item : items) { VideoVo vo = VideoVo.init(info, item); list.add(vo); } //4、构造返回 return ResponseEntity.ok(new PageResult(page,pagesize,0l,list)); } ``` ### 3.5 查看动态列表 #### ManageController ```java @GetMapping("/messages") public ResponseEntity messages(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pagesize, Long uid,Integer state ) { PageResult result = managerService.findAllMovements(page,pagesize,uid,state); return ResponseEntity.ok(result) } ``` #### ManagerService ```java //查询指定用户发布的所有动态 public ResponseEntity findAllMovements(Integer page, Integer pagesize, Long userId, Integer state) { //1、调用API查询 :(PageResult<Publish>) PageResult result = movementApi.findByUserId(userId,page,pagesize); //2、一个Publsh构造一个Movements List<Movement> items = ( List<Movement>)result.getItems(); List<MovementsVo> list = new ArrayList<>(); for (Movement item : items) { UserInfo userInfo = userInfoApi.findById(item.getUserId()); MovementsVo vo = MovementsVo.init(userInfo, item); list.add(vo); } //3、构造返回值 result.setItems(list); return ResponseEntity.ok(result); } ``` ``` 最后修改:2023 年 09 月 07 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏