TH9-搭建后台系统
- 1、项目架构
- 1.1 概述
- 1.2 API网关
- 1.2.1 搭建网关
- 依赖
- 引导类
- 跨域问题配置类
- 配置文件
- 1.2.2 配置鉴权管理器
- 1.3 Nacos配置中心
- 1.3.1 添加依赖
- 1.3.2 添加bootstrap.yml配置
- 1.3.3 nacos添加配置
- 2、后台系统
- 2.1 概述
- 2.2 环境前端搭建
- 2.2.1 导入数据库
- 2.2.2 导入静态页面
- nginx安装
- 测试
- 2.3 搭建后端环境
- 2.3.1 Admin实体类
- 2.3.2 导入项目
- 2.3.3 nacos中加入配置
- 2.4 管理员登录
- 2.4.1 生成验证码
- SystemController
- 2.4.2 用户登录
- SystemController
- SystemService
- 2.4.3 获取用户资料
- AdminVO
- SystemController
- SystemService
- 3、用户管理
- 3.1 需求分析
- 3.2 查询用户列表
- ManageController
- ManagerService
- UserInfoAPI
- 3.3 查看用户详情
- ManageController
- ManagerService
- 3.4 查看视频列表
- ManageController
- ManagerService
- 3.5 查看动态列表
- ManageController
- ManagerService
1、项目架构
1.1 概述
API网关有很多实现方式,我们通过SpringCloud Gateway实现
使用Nacos作为配置中心
1.2 API网关
1.2.1 搭建网关
依赖
<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>
引导类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
跨域问题配置类
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);
}
}
配置文件
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
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 配置鉴权管理器
@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可以快速实现服务发现、服务配置管理等需求
1.3.1 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
1.3.2 添加bootstrap.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添加配置
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建立的后台管理系统,目的是完成探花交友项目的业务闭环,主要功能包括:用户管理、动态管理、审核管理以及系统管理。
课程中实现的功能有:登录、首页、用户管理、动态审核。
2.2 环境前端搭建
2.2.1 导入数据库
将资料中的tanhua-admin.sql
引入到mysql数据库中
2.2.2 导入静态页面
后台系统也是采用前后端分离的方式,前端采用Vue.js实现,关于前端系统我们不进行实现,拿来直接使用。
nginx安装
将资料中提供的nginx解压到合适的位置
其中html目录中为,vue编译后的所有页面。
修改Nginx的/conf/nginx.conf
配置文件:
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页面的路径:localhost:8088
- 其中内部调用java服务器的路径 : http://127.0.0.1:18083/
测试
双击nginx.exe
,待启动完成后访问: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模块导入到探花项目中,完成开发。
2.3.3 nacos中加入配置
DataID:tanhua-admin-prod.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
/**
* 生成图片验证码
*/
@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
/**
* 用户登录:
*/
@PostMapping("/login")
public ResponseEntity login(@RequestBody Map map) {
Map retMap = adminService.login(map);
return ResponseEntity.ok(retMap);
}
SystemService
//用户登录
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
@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
/**
* 获取用户的信息
*/
@PostMapping("/profile")
public ResponseEntity profile() {
AdminVo vo = adminService.profile();
return ResponseEntity.ok(vo);
}
SystemService
//获取当前用户的用户资料
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
@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
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
@Override
public IPage<UserInfo> findAll(Integer page, Integer pagesize) {
return userInfoMapper.selectPage(new Page<UserInfo>(page,pagesize),null);
}
3.3 查看用户详情
ManageController
/**
* 根据id查询用户详情
*/
@GetMapping("/users/{userId}")
public ResponseEntity findById(@PathVariable("userId") Long userId) {
return managerService.findById(userId);
}
ManagerService
//根据id查询用户详情
public ResponseEntity findById(Long userId) {
UserInfo info = userInfoApi.findById(userId);
return ResponseEntity.ok(info);
}
3.4 查看视频列表
ManageController
/**
* 查询指定用户发布的所有视频列表
*/
@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
//根据用户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
@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
//查询指定用户发布的所有动态
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);
}