1、项目结构
> 该项目使用分模块设计
- Sky-take-out
- Sky-common
- Sky-pojo
- Sky-server
Sky-take-out
父模块,主要在其中导入项目需要的共同依赖、统一管理依赖版本、聚合子模块
Sky-common
存放公共类、工具类、异常类等
Sky-pojo
存放实体类VO、DTO等,POJO又根据不同的功能细分为以下几个对象
Entity:和数据库中数据对应的实体类
DTO:前后端之间传输数据的对象,如JSON
VTO:视图对象,由后端提供给前端显示的数据对象
Sky-server
存放配置文件如Controller、Service、Mapper等
2、开发环境搭建
2.1远程仓库搭建
1、在IDE中通过VSGIT将项目文件所在文件夹init为一个git仓库
2、在GitHub上创建sky-take-out远程仓库,并复制ssh仓库地址
3、将初始后端代码作为version-1提交到本地仓库,再通过push提交到远程仓库的main分支
这样远程仓库搭建完成
2.2数据库创建主要表和数据
address_book : 收货地址
category:菜品分类
dish:菜品
dish_flavor:菜品风味
employee:
order_detail
orders
setmeal
setmeal_dish
shopping_cart
user
3、登录功能的开发
请求路径:localhost:8080/admin/employee/login,Post请求方式携带Json的userName和passWord
大致流程:EmployeeController接收到请求后通过employeeDTO获取到携带的userName和passWord调用employeeService中的Login方法,其中主要是调用mapper层去数据库根据用户名查信息,并对信息是否正确做判断。如果employeeService查到了正确的数据则返回employeeEntity对象给EmployeeController,Controller拿到后生成jwt令牌并将信息封装到employeeLoginVO中返回给前端
生成jwt令牌的代码
String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims);
参数一:指定密钥
参数二:指定密钥有效时间
参数三:指定携带的自定义内容
createJWT方法
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定签名的时候使用的签名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的时间
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
// 设置过期时间
.setExpiration(exp);
return builder.compact();
}
4、登录拦截器的开发
作用:对非登录功能的请求做拦截操作,判断请求用户是否已登录过携带有有效令牌
4.1创建Intercepter
@Slf4j
@Component
public class AdminLoginToken implements HandlerInterceptor {
@Autowired
JwtProperties jwtProperties;
//preHandle方法控制请求的拦截和放行,返回true表示放行,返回false表示拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1、获取请求中的token
String token = request.getHeader(jwtProperties.getAdminTokenName());
//2、判断令牌是否存在
if(!StringUtils.hasLength(token)){
//不存在则返回401
log.info("无操作权限");
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
}
//3、存在则校验令牌是否有效
try {
JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
} catch (Exception e) {
e.printStackTrace();
log.info("令牌错误");
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
return false;
}
return true;
}
//postHandle方法主要是做请求经过后台业务处理后,返回给前台页面之前调用操作
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
4.2MD5加密方式
java提供的方法:
DigestUtils.md5DigestAsHex("123".getBytes())
数据库提供的方法
select md5('123')
MD5加密同一个密码会是相同的结果,且MD5加密后不可逆,因此校验密码的方式就变为:将传入的密码经过MD5加密后再和数据库中密码比较
由于每个字符串通过MD5加密后都相同,因此容易被穷举法暴力解出。为了安全性设置盐添加到初始密码中,既再将密码加密之前添加一段固定/随机的字符串到密码中,再进行加密
5、Nginx
5.1反向代理
将前端对动态资源的请求,先交给Nginx,再由Nginx转发交给后端服务器
提高访问速度:如果请求访问的静态资源,则Nginx可以直接处理
提高访问性能:压缩传输中的数据,使用时解压
负载均衡:将大量的请求按照我们的配置分配到集群服务器处理
保证服务器安全:Nginx挂在外网络,服务器挂在内网。无法直接访问服务器
5.2反向代理的配置
文件位置:nginx-1.20.2\conf\ngonx.conf
location /api/ {
proxy_pass http://localhost:8080:/admin/;
}
例如:http://localhost/api/employee 经过反向代理 http://localhost:8080/admin/employee
5.3负载均衡策略
轮询 | 默认方式:顺序分发请求 |
weight | 按照权重分发,权重越高分发请求越多 |
ip_hash | 根据IP分配,每个请求之后固定访问某个服务器 |
least_conn | 最少连接数分配,优先分配给现在处理请求数少的服务器 |
url_hash | 相同的url分配到同一个服务器 |
fair | 响应时间短的服务优先分配 |
6、使用Swagger生成接口文档
导入依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
在配置文件配置
@Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {
@Bean
public Docket docket(){
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket (DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage("com.sky.controller"))
.paths(PathSelectors.any())
.build();
return docket;
}
protected void addResourceHandlers(ResourceHandlerRegistry registry){
//swagger会生成静态资源,这里配置swagger的静态资源位置
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
}
配置生成的接口文档中文注释
- @Api(tags = "接口文档中会出现该内容"):使用在Controller、Service、Mapper类上
- @ApiModel ( tags = "接口文档中出现的中文内容" ) :使用在POJO类上
- @ApiMpdelProperty ( tags = "接口文档中出现的中文内容" ) : 使用在类的属性上,描述属性
- @ApiOperation ( tags = "接口文档中出现的中文内容") :使用在方法上,对方法描述