JavaWeb_LeadNews_Day1-接口测试, app端登录, 网关, 前端集成
搭建nacos
- 拉取镜像
docker pull nacos/nacos-server:1.2.0
- 创建容器
docker run --env MODE=standalone --name nacos --restart=always -d -p 8848:8848 nacos/nacos-server:1.2.0
MODE=standalone
: 单机版--restart=always
: 开机启动-p 8848:8848
: 映射端口-d
: 创建一个守护式容器在后台运行
搭建初始工程
heima-leadnews: 父工程, 统一管理项目依赖, 继承SpringBoot
- heima-leadnews-common: 通用配置
- heima-leadnews-feign-api: feign对外的接口
- heima-leadnews-model: pojo, dto
- heima-leadnews-utils: 通用工具
- heima-leadnews-gateway: 管理网关工程
- heima-leadnews-service: 管理微服务
- heima-leadnews-test: 测试案例
app登录
表结构
新建数据库leadnews_user
表名称 | 说明 |
---|---|
ap_user | APP用户信息表 |
ap_user_fan | APP用户粉丝信息表 |
ap_user_follow | APP用户关注信息表 |
ap_user_realname | APP实名认证信息表 |
密码加盐
- md5是不可逆加密, md5相同的密码每次加密都一样, 不安全
- 在md5的基础上手动加盐处理
- 根据用户生成salt(随机字符串), md5加密
密码+salt
搭建用户微服务
- 在
heima-leadnews-service
下创建模块heima-leadnews-user
- 启动类
@Slf4j @SpringBootApplication @EnableDiscoveryClient @MapperScan("com.heima.user.mapper") public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); log.info("项目启动成功"); } }
- bootstrap.yml
server: port: 51801 spring: application: name: leadnews-user cloud: nacos: discovery: server-addr: 192.168.174.133:8848 config: server-addr: 192.168.174.133:8848 file-extension: yml
- logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!--定义日志文件的存储地址,使用绝对路径--> <property name="LOG_HOME" value="e:/logs"/> <!-- Console 输出设置 --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <charset>utf8</charset> </encoder> </appender> <!-- 按照每天生成日志文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名--> <fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <!-- 异步输出 --> <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <queueSize>512</queueSize> <!-- 添加附加的appender,最多只能添加一个 --> <appender-ref ref="FILE"/> </appender> <logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false"> <appender-ref ref="CONSOLE"/> </logger> <logger name="org.springframework.boot" level="debug"/> <root level="info"> <!--<appender-ref ref="ASYNC"/>--> <appender-ref ref="FILE"/> <appender-ref ref="CONSOLE"/> </root> </configuration>
功能实现
public ResponseResult login(LoginDto loginDto) {
// 1. 正常登录 用户名和密码
if(StringUtils.isNotBlank(loginDto.getPassword()) && StringUtils.isNotBlank(loginDto.getPhone())){
// 1.1 根据手机号查询用户信息
ApUser dbUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, loginDto.getPhone()));
if(dbUser == null){
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST, "用户信息不存在");
}
// 1.2 比对密码
String salt = dbUser.getSalt();
String password = loginDto.getPassword();
if(!dbUser.getPassword().equals(DigestUtils.md5DigestAsHex((password+salt).getBytes()))){
return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
}
// 1.3 返回数据 jwt
String token = AppJwtUtil.getToken(dbUser.getId().longValue());
HashMap<String, Object> map = new HashMap<>();
map.put("token", token);
dbUser.setSalt("");
dbUser.setPassword("");
map.put("user", dbUser);
return ResponseResult.okResult(map);
}else{
// 2. 游客登录
HashMap<String, Object> map = new HashMap<>();
map.put("token", AppJwtUtil.getToken(0L));
return ResponseResult.okResult(map);
}
}
接口测试工具
postman
图形化工具
swagger
- 依赖
<!-- swagger --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency>
- 配置类
@Configuration @EnableSwagger2 public class SwaggerConfiguration { @Bean public Docket buildDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInfo()) .select() // 要扫描的API(Controller)基础包 .apis(RequestHandlerSelectors.basePackage("com.heima")) .paths(PathSelectors.any()) .build(); } private ApiInfo buildApiInfo() { Contact contact = new Contact("黑马程序员","",""); return new ApiInfoBuilder() .title("黑马头条-平台管理API文档") .description("黑马头条后台api") .contact(contact) .version("1.0.0").build(); } }
- 自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ... com.heima.common.swagger.SwaggerConfiguration
- 注解
// controller ... @Api(value = "app端用户登录", tags = "app端用户登录") public class ApUserLoginController { ... ... @ApiOperation("用户登录") public ResponseResult login(@RequestBody LoginDto loginDto) { ... } } // model public class LoginDto { @ApiModelProperty(value = "手机号", required = true) private String phone; @ApiModelProperty(value = "手机号", required = true) private String password; }
- 访问地址
http://localhost:51801/swagger-ui.html
knife4j
- 依赖
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> </dependency>
- 配置类
@Configuration @EnableSwagger2 @EnableKnife4j @Import(BeanValidatorPluginsConfiguration.class) public class Swagger2Configuration { @Bean(value = "defaultApi2") public Docket defaultApi2() { Docket docket=new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) //分组名称 .groupName("1.0") .select() //这里指定Controller扫描包路径 .apis(RequestHandlerSelectors.basePackage("com.heima")) .paths(PathSelectors.any()) .build(); return docket; } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("黑马头条API文档") .description("黑马头条API文档") .version("1.0") .build(); } }
- 自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ... com.heima.common.swagger.Swagger2Configuration
- 访问地址
http://localhost:51801/doc.html
app端网关
搭建网关
- 依赖
<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>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency>
- 配置
# bootstrap.yml server: port: 51601 spring: application: name: leadnews-app-gateway cloud: nacos: discovery: server-addr: 192.168.174.133:8848 config: server-addr: 192.168.174.133:8848 file-extension: yml # nacos leadnews-app-gateway.yml spring: cloud: gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedHeaders: "*" allowedOrigins: "*" allowedMethods: - GET - POST - DELETE - PUT - OPTION routes: # 平台管理 - id: user uri: lb://leadnews-user predicates: - Path=/user/** filters: - StripPrefix= 1
认证过滤器
@Component
public class AuthorizeFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 2. 判断是否登录
if(request.getURI().getPath().contains("/login")){
return chain.filter(exchange);
}
// 3. 获取token
String token = request.getHeaders().getFirst("token");
// 4. 判断token是否存在
if(StringUtils.isBlank(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 5. 判断token是否有效
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
int result = AppJwtUtil.verifyToken(claimsBody);
if(result==1 || result==2){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
} catch (Exception e) {
e.printStackTrace();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 6. 放行
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
nginx前端集成
在nginx安装的conf目录下新建一个文件夹leadnews.conf
,在当前文件夹中新建heima-leadnews-app.conf
文件
heima-leadnews-app.conf配置如下:
upstream heima-app-gateway{
server localhost:51601;
}
server {
listen 8801;
location / {
root D:/workspace/leadnews/app-web/;
index index.html;
}
location ~/app/(.*) {
proxy_pass http://heima-app-gateway/$1;
proxy_set_header HOST $host; # 不改变源请求头的值
proxy_pass_request_body on; #开启获取请求体
proxy_pass_request_headers on; #开启获取请求头
proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息
}
}
nginx.conf 把里面注释的内容和静态资源配置相关删除,引入heima-leadnews-app.conf文件加载
#user nobody;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
# 引入自定义配置文件
include leadnews.conf/*.conf;
}
打开前端项目进行测试 – > http://localhost:8801
来源
黑马程序员. 黑马头条