Nacos注册发现、网关
- a. 项目介绍
- b. app登录
- 1) 需求分析
- 2) 表结构分析
- 3) 手动加密(md5+随机字符串)
- 4) 用户端微服务搭建
- 5) 功能实现
- 6) app网关
- 7) 网关校验jwt
- 8) 前端集成, 配置nginx
a. 项目介绍
业务说明
技术栈说明
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k5BDy2q4-1691417838766)(C:\Users\captaindeng\AppData\Roaming\Typora\typora-user-images\image-20230807140029623.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yOvDt0kS-1691417838767)(C:\Users\captaindeng\AppData\Roaming\Typora\typora-user-images\image-20230807141737685.png)]
b. app登录
1) 需求分析
- 用户点击开始使用: 登录后的用户权限较大,可以查看,也可以操作(点赞,关注,评论)
- 用户点击不登录,先看看: 游客只有查看的权限
2) 表结构分析
关于app端用户相关的内容较多,可以单独设置一个库leadnews_user
表名称 | 说明 |
---|---|
ap_user | APP用户信息表 |
ap_user_fan | APP用户粉丝信息表 |
ap_user_follow | APP用户关注信息表 |
ap_user_realname | APP实名认证信息表 |
app_user表对应的实体类如下:
/**
* @author Kyle
* @date 2023/8/7 14:35
*
* App用户信息表
*/
@Data
@TableName("ap_user")
public class ApUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 密码、通信等加密盐
*/
@TableField("salt")
private String salt;
/**
* 用户名
*/
@TableField("name")
private String name;
/**
* 密码,md5加密
*/
@TableField("password")
private String password;
/**
* 手机号
*/
@TableField("phone")
private String phone;
/**
* 头像
*/
@TableField("image")
private String image;
/**
* 0 男
1 女
2 未知
*/
@TableField("sex")
private Boolean sex;
/**
* 0 未
1 是
*/
@TableField("is_certification")
private Boolean certification;
/**
* 是否身份认证
*/
@TableField("is_identity_authentication")
private Boolean identityAuthentication;
/**
* 0正常
1锁定
*/
@TableField("status")
private Boolean status;
/**
* 0 普通用户
1 自媒体人
2 大V
*/
@TableField("flag")
private Short flag;
/**
* 注册时间
*/
@TableField("created_time")
private Date createdTime;
}
3) 手动加密(md5+随机字符串)
md5是不可逆加密,md5相同的密码每次加密都一样,不太安全。在md5的基础上手动加盐(salt)处理
注册->生成盐
登录->使用盐来配合验证
4) 用户端微服务搭建
在heima-leadnews-service下创建名为heima-leadnews-user的module
在com.heima.user下创建UserApplication
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.user.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
创建resources/bookstrap.yml
server:
port: 51801
spring:
application:
name: leadnews-user
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
config:
server-addr: 192.168.200.130:8848
file-extension: yml
在nacos网站中创建配置文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.user.pojos
创建resources/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>
5) 功能实现
- 1.用户输入了用户名和密码进行登录,校验成功后返回jwt(基于当前用户的id生成)
- 2.用户游客登录,生成jwt返回(基于默认值0生成)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P4bv6u47-1691417838767)(C:\Users\captaindeng\AppData\Roaming\Typora\typora-user-images\image-20230807153653965.png)]
接口定义
说明 | |
---|---|
接口路径 | /api/v1/login/login_auth |
请求方式 | POST |
参数 | LoginDto |
响应结果 | ResponseResult |
实现步骤
0.在model层下创建user.dtos的LoginDto
@Data
public class LoginDto {
/**
* 手机号
*/
private String phone;
/**
* 密码
*/
private String password;
}
1.在controller.v1下创建ApUserLoginController,接口定义
@RestController
@RequestMapping("/api/v1/login")
public class ApUserLoginController {
@Autowired
private ApUserService apUserService;
@PostMapping("/login_auth")
public ResponseResult login(@RequestBody LoginDto loginDto){
return apUserService.login(loginDto);
}
}
2.持久层mapper下创建ApUserMapper
@Mapper
public interface ApUserMapper extends BaseMapper<ApUser> {
}
3.业务层service
public interface ApUserService extends IService<ApUser> {
/**
* App端登录功能
* @param loginDto
* @return
*/
public ResponseResult login(LoginDto loginDto);
}
4.实现类
@Service
@Transactional
@Slf4j
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {
/**
* App端登录功能
* @param loginDto
* @return
*/
@Override
public ResponseResult login(LoginDto loginDto) {
// 1.正常登录:用户名和密码
// 1.1 根据手机号查询用户信息
if (StringUtils.isNotBlank(loginDto.getPhone()) && StringUtils.isNotBlank(loginDto.getPassword())){
ApUser dbUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, loginDto.getPhone()));
if (dbUser == null){
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "用户信息不存在");
}
// 1.2 比对密码
String salt = dbUser.getSalt();
String password = loginDto.getPassword();
String pwsd = DigestUtils.md5DigestAsHex((password + salt).getBytes());
if (!pwsd.equals(dbUser.getPassword())){
// 密码错误
return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
}
// 1.3 返回数据 jwt user
String token = AppJwtUtil.getToken(dbUser.getId().longValue());
Map<String, Object> map = new HashMap<>();
map.put("token", token);
dbUser.setSalt("");
dbUser.setPassword("");
map.put("user", dbUser);
return ResponseResult.okResult(map);
}else {
// 2.游客登录
Map<String, Object> map = new HashMap<>();
map.put("token", AppJwtUtil.getToken(0L));
return ResponseResult.okResult(map);
}
}
}
6) app网关
网关概述
1.在heima-leadnews-gateway导入以下依赖
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
(2)在heima-leadnews-gateway下创建heima-leadnews-app-gateway微服务
引导类:
@SpringBootApplication
@EnableDiscoveryClient //开启注册中心
public class AppGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(AppGatewayApplication.class,args);
}
}
bootstrap.yml
server:
port: 51601
spring:
application:
name: leadnews-app-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.200.130:8848
config:
server-addr: 192.168.200.130:8848
file-extension: yml
在nacos的配置中心创建dataid为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
7) 网关校验jwt
具体实现:
第一:
在认证过滤器中需要用到jwt的解析,所以需要把工具类拷贝一份到网关微服务
第二:
在网关微服务中新建全局过滤器:在gateway/fliter下创建
@Component
@Slf4j
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.isNotBlank(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);
}
/**
* 优先级设置 值越小 优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
8) 前端集成, 配置nginx
①:解压资料文件夹中的压缩包nginx-1.18.0.zip
②:解压资料文件夹中的前端项目app-web.zip
③:配置nginx.conf文件
在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/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;
}
④ :启动nginx
在nginx安装包中使用命令提示符打开,输入命令nginx启动项目
可查看进程,检查nginx是否启动
重新加载配置文件:nginx -s reload
⑤:打开前端项目进行测试 – > http://localhost:8801
用谷歌浏览器打开,调试移动端模式进行访问