您的位置:首页 > 健康 > 美食 > 十、前后端分离通用权限系统(10)

十、前后端分离通用权限系统(10)

2025/2/24 22:25:35 来源:https://blog.csdn.net/weixin_42171159/article/details/141691953  浏览:    关键词:十、前后端分离通用权限系统(10)

🌻🌻 目录

  • 一、功能说明
  • 二、部门管理
  • 三、岗位管理
  • 四、日志管理
    • 4.1、登录日志
      • 4.1.1、功能实现- 接口
      • 4.1.2、编写 AsyncLoginLogMapper
      • 4.1.3、功能实现- 接口实现
      • 4.1.4、导入获取ip地址的工具类 IpUtil
      • 4.1.5、在 TokenLoginFilter 调用方法实现
      • 4.1.6、修改 WebSecurityConfig
      • 4.1.7、测试
    • 4.2、操作日志实现
      • 4.2.1、创建common-log 模块
      • 4.2.2、创建自定义注解Log
      • 4.2.3、创建AOP类 LogAspect
      • 4.2.4、创建枚举类
      • 4.2.3、创建AOP类LogAspect
      • 4.2.4、创建接口 AsyncOperLogService
      • 4.2.5、编写mapper
      • 4.2.6、编写接口实现
      • 4.2.7、进行简单测试
  • 五、项目部署
    • 5.1 后端打包
    • 5.2 前端打包

一、功能说明

其他功能:部门管理、岗位管理、日志管理(登录日志、操作日志)

二、部门管理

实现方式与菜单管理类似

三、岗位管理

实现方式与菜单管理类似

四、日志管理

4.1、登录日志

4.1.1、功能实现- 接口

在spring-security 模块下面创建包 com.gansu.system.service 再创建 AsyncLoginLogService

在这里插入图片描述

package com.gansu.system.service;public interface AsyncLoginLogService {public void recordLoginLog(String username,String ipaddr,Integer status,String message);
}

4.1.2、编写 AsyncLoginLogMapper

AsyncLoginLogMapper

在这里插入图片描述

package com.gansu.system.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gansu.model.system.SysLoginLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;@Repository
@Mapper
public interface AsyncLoginLogMapper extends BaseMapper<SysLoginLog> {public void recordLoginLog(String username,String ipaddr,Integer status,String message);
}

4.1.3、功能实现- 接口实现

service-system实现添加日志方法

在这里插入图片描述

package com.gansu.system.service.impl;import com.gansu.model.system.SysLoginLog;
import com.gansu.system.mapper.AsyncLoginLogMapper;
import com.gansu.system.service.AsyncLoginLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class AsyncLoginLogServiceImpl implements AsyncLoginLogService {@Autowiredprivate AsyncLoginLogMapper asyncLoginLogMapper;@Overridepublic void recordLoginLog(String username, String ipaddr, Integer status, String message) {SysLoginLog sysLoginLog = new SysLoginLog();sysLoginLog.setUsername(username);sysLoginLog.setIpaddr(ipaddr);sysLoginLog.setStatus(status);sysLoginLog.setMsg(message);asyncLoginLogMapper.insert(sysLoginLog);}
}

4.1.4、导入获取ip地址的工具类 IpUtil

在这里插入图片描述

package com.gansu.system.utils;import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;/*** 获取ip地址*/
public class IpUtil {public static String getIpAddress(HttpServletRequest request) {String ipAddress = null;try {ipAddress = request.getHeader("x-forwarded-for");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();if (ipAddress.equals("127.0.0.1")) {// 根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();} catch (UnknownHostException e) {e.printStackTrace();}ipAddress = inet.getHostAddress();}}// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()// = 15if (ipAddress.indexOf(",") > 0) {ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));}}} catch (Exception e) {ipAddress="";}// ipAddress = this.getRequest().getRemoteAddr();return ipAddress;}public static String getGatwayIpAddress(ServerHttpRequest request) {HttpHeaders headers = request.getHeaders();String ip = headers.getFirst("x-forwarded-for");if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {// 多次反向代理后会有多个ip值,第一个ip才是真实ipif (ip.indexOf(",") != -1) {ip = ip.split(",")[0];}}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("X-Real-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddress().getAddress().getHostAddress();}return ip;}
}

4.1.5、在 TokenLoginFilter 调用方法实现

在这里插入图片描述

package com.gansu.system.fillter;import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gansu.common.result.Result;
import com.gansu.common.result.ResultCodeEnum;
import com.gansu.common.utils.JwtHelperUtils;
import com.gansu.common.utils.ResponseUtil;
import com.gansu.model.vo.LoginVo;
import com.gansu.system.custom.CustomUser;
import com.gansu.system.service.AsyncLoginLogService;
import com.gansu.system.utils.IpUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;//4.自定义认证过滤器//登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {private RedisTemplate redisTemplate;private AsyncLoginLogService asyncLoginLogService;//构造public TokenLoginFilter(AuthenticationManager authenticationManager,RedisTemplate redisTemplate,AsyncLoginLogService asyncLoginLogService) {this.setAuthenticationManager(authenticationManager);this.setPostOnly(false);//指定登录接口及提交方式,可以指定任意路径this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login","POST"));this.redisTemplate = redisTemplate;this.asyncLoginLogService = asyncLoginLogService;}//获取用户名和密码进行认证@Overridepublic Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)throws AuthenticationException {try {LoginVo loginVo = new ObjectMapper().readValue(req.getInputStream(), LoginVo.class);Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());return this.getAuthenticationManager().authenticate(authenticationToken);} catch (IOException e) {throw new RuntimeException(e);}}//认证成功@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,Authentication auth) throws IOException, ServletException {//获取认证对象CustomUser customUser = (CustomUser) auth.getPrincipal();//生成tokenString token = JwtHelperUtils.createToken(customUser.getSysUser().getId(),customUser.getSysUser().getUsername());//保存权限数据redisTemplate.opsForValue().set(customUser.getUsername(),JSON.toJSONString(customUser.getAuthorities()));asyncLoginLogService.recordLoginLog(customUser.getUsername(),IpUtil.getIpAddress(request),1,"登录成功");//返回Map<String, Object> map = new HashMap<>();map.put("token", token);ResponseUtil.out(response, Result.ok(map));}//认证失败@Overrideprotected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,AuthenticationException e) throws IOException, ServletException {if(e.getCause() instanceof RuntimeException) {ResponseUtil.out(response, Result.build(null, 204, e.getMessage()));} else {ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_MOBLE_ERROR));}}
}

4.1.6、修改 WebSecurityConfig

在这里插入图片描述

package com.gansu.system.config;import com.gansu.system.custom.CustomMd5PasswordEncoder;
import com.gansu.system.fillter.TokenAuthenticationFilter;
import com.gansu.system.fillter.TokenLoginFilter;
import com.gansu.system.service.AsyncLoginLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity //@EnableWebSecurity是开启SpringSecurity的默认行为
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解功能,默认禁用注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate CustomMd5PasswordEncoder customMd5PasswordEncoder;@Autowiredprivate AsyncLoginLogService asyncLoginLogService;@Bean@Overrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}@Overrideprotected void configure(HttpSecurity http) throws Exception {// 这是配置的关键,决定哪些接口开启防护,哪些接口绕过防护http//关闭csrf.csrf().disable()// 开启跨域以便前端调用接口.cors().and().authorizeRequests()// 指定某些接口不需要通过验证即可访问。登陆接口肯定是不需要认证的.antMatchers("/admin/system/index/login").permitAll()// 这里意思是其它所有接口需要认证才能访问.anyRequest().authenticated().and()//TokenAuthenticationFilter放到UsernamePasswordAuthenticationFilter的前面,// 这样做就是为了除了登录的时候去查询数据库外,其他时候都用token进行认证。.addFilterBefore(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class).addFilter(new TokenLoginFilter(authenticationManager(),redisTemplate,asyncLoginLogService));//禁用sessionhttp.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {// 指定UserDetailService和加密器auth.userDetailsService(userDetailsService).passwordEncoder(customMd5PasswordEncoder);}/*** 配置哪些请求不拦截* 排除swagger相关请求* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html");}
}

4.1.7、测试

测试前数据库:

在这里插入图片描述

登录成功后数据库:

在这里插入图片描述

在这里插入图片描述

登录日志前端 实现方式参考菜单管理

4.2、操作日志实现

系统引入common-log模块,采用AOP及自定义标签实现操作日志记录

4.2.1、创建common-log 模块

在这里插入图片描述

在这里插入图片描述

引入依赖

在这里插入图片描述

<dependencies><dependency><groupId>com.gansu</groupId><artifactId>model</artifactId><version>1.0</version></dependency><dependency><groupId>com.gansu</groupId><artifactId>common-util</artifactId><version>1.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

4.2.2、创建自定义注解Log

在这里插入图片描述

package com.gansu.system.annotation;import com.gansu.system.enums.BusinessType;
import com.gansu.system.enums.OperatorType;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义操作日志记录注解*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {/*** 模块*/public String title() default "";/*** 功能*/public BusinessType businessType() default BusinessType.OTHER;/*** 操作人类别*/public OperatorType operatorType() default OperatorType.MANAGE;/*** 是否保存请求的参数*/public boolean isSaveRequestData() default true;/*** 是否保存响应的参数*/public boolean isSaveResponseData() default true;
}

4.2.3、创建AOP类 LogAspect

在这里插入图片描述

4.2.4、创建枚举类

在这里插入图片描述

BusinessType

package com.gansu.system.enums;/*** 业务操作类型*/
public enum BusinessType {/*** 其它*/OTHER,/*** 新增*/INSERT,/*** 修改*/UPDATE,/*** 删除*/DELETE,/*** 授权*/ASSGIN,/*** 导出*/EXPORT,/*** 导入*/IMPORT,/*** 强退*/FORCE,/*** 更新状态*/STATUS,/*** 清空数据*/CLEAN,
}

OperatorType

package com.gansu.system.enums;/*** 操作人类别*/
public enum OperatorType {/*** 其它*/OTHER,/*** 后台用户*/MANAGE,/*** 手机端用户*/MOBILE
}

4.2.3、创建AOP类LogAspect

在这里插入图片描述

package com.gansu.system.aspect;import java.util.Collection;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.gansu.common.utils.IpUtil;
import com.gansu.common.utils.JwtHelperUtils;
import com.gansu.model.system.SysOperLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.gansu.system.annotation.Log;
import com.gansu.system.service.AsyncOperLogService;/*** 操作日志记录处理*/
@Aspect
@Component
public class LogAspect {private static final Logger log = LoggerFactory.getLogger(LogAspect.class);//微服务切换为feign调用接口@Resourceprivate AsyncOperLogService asyncOperLogService;/*** 处理完请求后执行** @param joinPoint 切点*/@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {handleLog(joinPoint, controllerLog, null, jsonResult);}/*** 拦截异常操作** @param joinPoint 切点* @param e         异常*/@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {handleLog(joinPoint, controllerLog, e, null);}protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {try {RequestAttributes ra = RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra = (ServletRequestAttributes) ra;HttpServletRequest request = sra.getRequest();// *========数据库日志=========*//SysOperLog operLog = new SysOperLog();operLog.setStatus(1);// 请求的地址String ip = IpUtil.getIpAddress(request);//IpUtil.getIpAddr(ServletUtils.getRequest());operLog.setOperIp(ip);operLog.setOperUrl(request.getRequestURI());String token = request.getHeader("token");String userName = JwtHelperUtils.getUsername(token);operLog.setOperName(userName);if (e != null) {operLog.setStatus(0);operLog.setErrorMsg(e.getMessage());}// 设置方法名称String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();operLog.setMethod(className + "." + methodName + "()");// 设置请求方式operLog.setRequestMethod(request.getMethod());// 处理设置注解上的参数getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);// 保存数据库// asyncOperLogService.saveSysLog(operLog);} catch (Exception exp) {// 记录本地异常日志log.error("==前置通知异常==");log.error("异常信息:{}", exp.getMessage());exp.printStackTrace();}}/*** 获取注解中对方法的描述信息 用于Controller层注解** @param log     日志* @param operLog 操作日志* @throws Exception*/public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception {// 设置action动作operLog.setBusinessType(log.businessType().name());// 设置标题operLog.setTitle(log.title());// 设置操作人类别operLog.setOperatorType(log.operatorType().name());// 是否需要保存request,参数和值if (log.isSaveRequestData()) {// 获取参数的信息,传入到数据库中。setRequestValue(joinPoint, operLog);}// 是否需要保存response,参数和值if (log.isSaveResponseData() && !StringUtils.isEmpty(jsonResult)) {operLog.setJsonResult(JSON.toJSONString(jsonResult));}}/*** 获取请求的参数,放到log中** @param operLog 操作日志* @throws Exception 异常*/private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {String requestMethod = operLog.getRequestMethod();if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {String params = argsArrayToString(joinPoint.getArgs());operLog.setOperParam(params);}}/*** 参数拼装*/private String argsArrayToString(Object[] paramsArray) {String params = "";if (paramsArray != null && paramsArray.length > 0) {for (Object o : paramsArray) {if (!StringUtils.isEmpty(o) && !isFilterObject(o)) {try {Object jsonObj = JSON.toJSON(o);params += jsonObj.toString() + " ";} catch (Exception e) {}}}}return params.trim();}/*** 判断是否需要过滤的对象。** @param o 对象信息。* @return 如果是需要过滤的对象,则返回true;否则返回false。*/@SuppressWarnings("rawtypes")public boolean isFilterObject(final Object o) {Class<?> clazz = o.getClass();if (clazz.isArray()) {return clazz.getComponentType().isAssignableFrom(MultipartFile.class);} else if (Collection.class.isAssignableFrom(clazz)) {Collection collection = (Collection) o;for (Object value : collection) {return value instanceof MultipartFile;}} else if (Map.class.isAssignableFrom(clazz)) {Map map = (Map) o;for (Object value : map.entrySet()) {Map.Entry entry = (Map.Entry) value;return entry.getValue() instanceof MultipartFile;}}return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse|| o instanceof BindingResult;}
}

4.2.4、创建接口 AsyncOperLogService

在这里插入图片描述

package com.gansu.system.service;import com.gansu.model.system.SysOperLog;public interface AsyncOperLogService{public void saveSysLog(SysOperLog sysOperLog);}

4.2.5、编写mapper

① 引入 common-logservice-util

在这里插入图片描述

<dependency><groupId>com.gansu</groupId><artifactId>common-log</artifactId><version>1.0</version>
</dependency>

② 编写mapper

在这里插入图片描述

package com.gansu.system.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gansu.model.system.SysOperLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;@Repository
@Mapper
public interface AsyncOperLogMapper extends BaseMapper<SysOperLog>{public void saveSysLog(SysOperLog sysOperLog);
}

4.2.6、编写接口实现

编写 AsyncOperLogService 接口实现 AsyncOperLogServiceImpl

在这里插入图片描述

package com.gansu.system.service.impl;import com.gansu.model.system.SysOperLog;
import com.gansu.system.mapper.AsyncOperLogMapper;
import com.gansu.system.service.AsyncOperLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class AsyncOperLogServiceImpl implements AsyncOperLogService {@Autowiredprivate AsyncOperLogMapper asyncOperLogMapper;@Overridepublic void saveSysLog(SysOperLog sysOperLog) {asyncOperLogMapper.saveSysLog(sysOperLog);}
}

4.2.7、进行简单测试

① 先清楚数据库之前的操作数据

在这里插入图片描述

② 进行一个角色的添加,例如:

在这里插入图片描述

 @Log(title = "添加角色操纵",businessType = BusinessType.INSERT)  //日志管理,进行添加的记录

在这里插入图片描述
数据库应该会增加一条:

在这里插入图片描述

在这里插入图片描述

五、项目部署

在这里插入图片描述

5.1 后端打包

① 打包步骤:

在这里插入图片描述

② 运行显示

在这里插入图片描述

5.2 前端打包

① 修改如下为 prod-api

在这里插入图片描述

② 进行打包,命令为 npm run build:prod

在这里插入图片描述

完成打包:

在这里插入图片描述

③ 将打包后的文件及其文件夹复制到解压后的nginx下面的html下面

在这里插入图片描述

④ 修改 nginx下面的nginx.conf 配置

在这里插入图片描述

location /prod-api/ {proxy_pass   http://localhost:8800/;}

⑤ 启动nginx nginx.exe

在这里插入图片描述

⑥ 直接浏览器访问: http://localhost/

在这里插入图片描述

接口测试文档也可以访问: http://localhost:8800/doc.html

在这里插入图片描述

nginx 关机 nginx.exe -s stop

在这里插入图片描述

文章源码

系统文档资料

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com