第四课 Spring Cloud分布式微服务实战- 开发admin管理服务
tags:
- Java
- 慕课网
categories:
- admin服务
第一节 admin账号操作
1.1 admin工程构建和表结构
- 新建
imooc-news-dev-service-admin服务。- 引入依赖,复制配置文件,修改端口,日志配置,复制一份启动类。
- 创建controller, 把hellocontroler弄过来。运行测试访问 /hello
<dependencies>
<dependency>
<groupId>com.imooc</groupId>
<artifactId>imooc-news-dev-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
-
下列表结构中添加一条数据。

-
写个工具类,测试从spring的security引入安全加密包对明文密码加密。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
package com.imooc.admin.controller;
import org.springframework.security.crypto.bcrypt.BCrypt;
public class PWDTest {
public static void main(String[] args) {
String pwd = BCrypt.hashpw("admin", BCrypt.gensalt());
System.out.println(pwd);
}
}
1.2 逆向生成持久层文件
- window下修改conf所在目录路径为绝对路径,conf里面的生成文件路径为绝对路径。
- 第一:先把
com.imooc.admin.mapper.AdminUserMapper下Mapper复制到admin项目下。 - 第二:把
com.imooc.pojo.AdminUser下AdminUser类复制到model下的pojo中 - 第三:把
resources\mapper\admin\AdminUserMapper.xml复制到resources\mapper\AdminUserMapper.xml - 创建service接口
com.imooc.admin.service.AdminUserService
package com.imooc.admin.service;
import com.imooc.pojo.AdminUser;
public interface AdminUserService {
/**
* 获得管理员有用户信息
* @param username
* @return
*/
public AdminUser queryAdminByUsername(String username);
}
- 实现service。这里注意如果
adminUserMapper找不到。去AdminUserMapper上加上@Repository注入到容器中。
package com.imooc.admin.service.impl;
import com.imooc.admin.mapper.AdminUserMapper;
import com.imooc.admin.service.AdminUserService;
import com.imooc.pojo.AdminUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;
@Service
public class AdminUserServiceImpl implements AdminUserService {
@Autowired
public AdminUserMapper adminUserMapper;
@Override
public AdminUser queryAdminByUsername(String username) {
Example adminExample = new Example(AdminUser.class);
Example.Criteria criteria = adminExample.createCriteria();
criteria.andEqualTo("username", username);
AdminUser admin = adminUserMapper.selectOneByExample(adminExample);
return admin;
}
}
1.3 用户登陆验证和用户唯一查询实现
- api写接口。
package com.imooc.api.controller.admin;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.pojo.bo.AdminLoginBO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Api(value = "管理员admin维护", tags = {"管理员admin维护的controller"})
@RequestMapping("adminMng")
public interface AdminMngControllerApi {
@ApiOperation(value = "admin登陆的接口", notes = "admin登陆的接口", httpMethod = "POST")
@PostMapping("/adminLogin")
public GraceJSONResult adminLogin(@RequestBody AdminLoginBO adminLoginBO,
HttpServletRequest request,
HttpServletResponse response);
@ApiOperation(value = "admin登陆的接口", notes = "admin登陆的接口", httpMethod = "POST")
@PostMapping("/adminIsExist")
public GraceJSONResult adminIsExist(@RequestParam String username);
}
- model中写一个BO,
AdminLoginBO
package com.imooc.pojo.bo;
/**
* 管理员登录的BO
*/
public class AdminLoginBO {
private String username;
private String password;
private String img64;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getImg64() {
return img64;
}
public void setImg64(String img64) {
this.img64 = img64;
}
}
- admin中写一个实现
AdminMngController, 包括验证账号密码、储存cookie到redis、验证账号是否存在。
package com.imooc.admin.controller;
import com.imooc.admin.service.AdminUserService;
import com.imooc.api.BaseController;
import com.imooc.api.controller.admin.AdminMngControllerApi;
import com.imooc.exception.GraceException;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.AdminUser;
import com.imooc.pojo.bo.AdminLoginBO;
import com.imooc.utils.RedisOperator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@RestController
public class AdminMngController extends BaseController implements AdminMngControllerApi {
final static Logger logger = LoggerFactory.getLogger(AdminMngController.class);
@Autowired
private AdminUserService adminUserService;
@Autowired
private RedisOperator redis;
@Override
public GraceJSONResult adminLogin(AdminLoginBO adminLoginBO,
HttpServletRequest request,
HttpServletResponse response) {
// 0. TODO 验证BO中的用户名和密码不为空
// 1. 查询admin用户的信息
AdminUser admin = adminUserService.queryAdminByUsername(adminLoginBO.getUsername());
// 2. 判断admin不为空,如果为空则登录失败
if (admin == null) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_NOT_EXIT_ERROR);
}
// 3. 判断密码是否匹配
boolean isPwdMatch = BCrypt.checkpw(adminLoginBO.getPassword(), admin.getPassword());
if (isPwdMatch) {
doLoginSettings(admin, request, response);
return GraceJSONResult.ok();
} else {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_NOT_EXIT_ERROR);
}
}
/**
* 用于admin用户登录过后的基本信息设置
* @param admin
* @param request
* @param response
*/
private void doLoginSettings(AdminUser admin,
HttpServletRequest request,
HttpServletResponse response) {
// 保存token放入到redis中
String token = UUID.randomUUID().toString();
redis.set(REDIS_ADMIN_TOKEN + ":" + admin.getId(), token);
// 保存admin登录基本token信息到cookie中
setCookie(request, response, "atoken", token, COOKIE_MONTH);
setCookie(request, response, "aid", admin.getId(), COOKIE_MONTH);
setCookie(request, response, "aname", admin.getAdminName(), COOKIE_MONTH);
}
@Override
public GraceJSONResult adminIsExist(String username) {
checkAdminExist(username);
return GraceJSONResult.ok();
}
private void checkAdminExist(String username) {
AdminUser admin = adminUserService.queryAdminByUsername(username);
if (admin != null) {
GraceException.display(ResponseStatusEnum.ADMIN_USERNAME_EXIST_ERROR);
}
}
}
1.4 新建查询注销admin账号
- api中添加。这里注意查询时候需要增加
@ApiParam用来接收分页参数。
@ApiOperation(value = "创建admin", notes = "创建admin", httpMethod = "POST")
@PostMapping("/addNewAdmin")
public GraceJSONResult addNewAdmin(@RequestBody NewAdminBO newAdminBO,
HttpServletRequest request,
HttpServletResponse response);
@ApiOperation(value = "查询admin列表", notes = "查询admin列表", httpMethod = "POST")
@PostMapping("/getAdminList")
public GraceJSONResult getAdminList(@ApiParam(name = "page", value = "查询下一页地址", required = false)
@RequestParam Integer age,
@ApiParam(name = "pageSize", value = "分页查询每一页的条数", required = false)
@RequestParam Integer pageSize);
@ApiOperation(value = "admin退出登陆", notes = "admin退出登陆", httpMethod = "POST")
@PostMapping("/adminLogout")
public GraceJSONResult adminLogout(@RequestParam String adminId,
HttpServletRequest request,
HttpServletResponse response);
- common引入返回前端的工具类
com.imooc.utils.PagedGridResult,我们的配置之前就有配置分页插件和一些分页配置。 - admin服务中controller实现上面方法。
@Override
public GraceJSONResult addNewAdmin(NewAdminBO newAdminBO,
HttpServletRequest request,
HttpServletResponse response) {
// 0. TODO 验证BO中的用户名和密码不为空
// 1. base64不为空,则代表人脸入库,否则需要用户输入密码和确认密码
if (StringUtils.isBlank(newAdminBO.getImg64())) {
if (StringUtils.isBlank(newAdminBO.getPassword()) ||
StringUtils.isBlank(newAdminBO.getConfirmPassword())
) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_PASSWORD_NULL_ERROR);
}
}
// 2. 密码不为空,则必须判断两次输入一致
if (StringUtils.isNotBlank(newAdminBO.getPassword())) {
if (!newAdminBO.getPassword()
.equalsIgnoreCase(newAdminBO.getConfirmPassword())) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_PASSWORD_ERROR);
}
}
// 3. 校验用户名唯一
checkAdminExist(newAdminBO.getUsername());
// 4. 调用service存入admin信息
adminUserService.createAdminUser(newAdminBO);
return GraceJSONResult.ok();
}
@Override
public GraceJSONResult getAdminList(Integer page, Integer pageSize) {
if (page == null) {
page = COMMON_START_PAGE;
}
if (pageSize == null) {
pageSize = COMMON_PAGE_SIZE;
}
PagedGridResult result = adminUserService.queryAdminList(page, pageSize);
return GraceJSONResult.ok(result);
}
@Override
public GraceJSONResult adminLogout(String adminId,
HttpServletRequest request,
HttpServletResponse response) {
// 从redis中删除admin的会话token
redis.del(REDIS_ADMIN_TOKEN + ":" + adminId);
// 从cookie中清理adming登录的相关信息
deleteCookie(request, response, "atoken");
deleteCookie(request, response, "aid");
deleteCookie(request, response, "aname");
return GraceJSONResult.ok();
}
- admin服务中service新加接口, 对数据库操作
/**
* 新增管理员
*/
public void createAdminUser(NewAdminBO newAdminBO);
/**
* 分页查询admin列表
*/
public PagedGridResult queryAdminList(Integer page, Integer pageSize);
- admin服务中service新加接口实现。
@Transactional
@Override
public void createAdminUser(NewAdminBO newAdminBO) {
String adminId = sid.nextShort();
AdminUser adminUser = new AdminUser();
adminUser.setId(adminId);
adminUser.setUsername(newAdminBO.getUsername());
adminUser.setAdminName(newAdminBO.getAdminName());
// 如果密码不为空,则需要加密密码,存入数据库
if (StringUtils.isNotBlank(newAdminBO.getPassword())) {
String pwd = BCrypt.hashpw(newAdminBO.getPassword(), BCrypt.gensalt());
adminUser.setPassword(pwd);
}
// 如果人脸上传以后,则有faceId,需要和admin信息关联存储入库
if (StringUtils.isNotBlank(newAdminBO.getFaceId())) {
adminUser.setFaceId(newAdminBO.getFaceId());
}
adminUser.setCreatedTime(new Date());
adminUser.setUpdatedTime(new Date());
int result = adminUserMapper.insert(adminUser);
if (result != 1) {
GraceException.display(ResponseStatusEnum.ADMIN_CREATE_ERROR);
}
}
@Override
public PagedGridResult queryAdminList(Integer page, Integer pageSize) {
Example adminExample = new Example(AdminUser.class);
adminExample.orderBy("createdTime").desc();
PageHelper.startPage(page, pageSize);
List<AdminUser> adminUserList =
adminUserMapper.selectByExample(adminExample);
return setterPagedGrid(adminUserList, page);
}
private PagedGridResult setterPagedGrid(List<?> adminUserList,
Integer page) {
PageInfo<?> pageList = new PageInfo<>(adminUserList);
PagedGridResult gridResult = new PagedGridResult();
gridResult.setRows(adminUserList);
gridResult.setPage(page);
gridResult.setRecords(pageList.getPages());
gridResult.setTotal(pageList.getTotal());
return gridResult;
}
- Api服务中BaseController添加工具类,删除cookie.
public void deleteCookie(HttpServletRequest request,
HttpServletResponse response,
String cookieName) {
try {
String deleteValue = URLEncoder.encode("", "utf-8");
setCookieValue(request, response, cookieName, deleteValue, COOKIE_DELETE);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
第二节 admin账号人脸登陆和入库
2.1 Mongodb介绍和环境搭建
- 谷歌浏览器开启视频调试模式。输入网址:
chrome://flags/#unsafely-treat-insecure-origin-as-secure。把域名加入解决域名不安全的问题,注意这里每次重启电脑和浏览器都需要重新加入。如果有证书就不用设置这个。
http://admin.imoocnews.com:9090/,http://admin.imoocnews.com

2. MongoDB介绍
- NoSql数据库
- 内存级别查询
- 不支持事务
- GridFS 小文件存储

3. MongoDB数据结构

4. MongoDB安装:
# 从镜像创建并启动mongoDb容器
docker run -d --privileged=true -p 27017:27017 --name=mongodb mongo:latest
#admin进入
docker exec -it 05977a5479 /bin/bash
# 连接数据库
mongo
# 使用默认数据库admin
use admin
#创建用户和密码
db.createUser({user:"root",pwd:"root",roles:[{role:"root",db:"admin"}]})
#验证一下对不对
db.auth("root","root")
show users
#退出
exit
- navicat连接一下, 设置密码连接。右键新建数据库
imooc-news。
2.2 整合GridFS
- 把顶级项目的mongodb的依赖配置拿到
imooc-news-dev-model中。
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver</artifactId>
</dependency>
imooc-news-dev-service-api中controller中files
/**
* 文件上传到mongodb的gridfs中
* 这个我们不能通过swagger2调用 就用不写@ApiOperation
* @param newAdminBO
* @return
*/
@PostMapping("/uploadToGridFS")
public GraceJSONResult uploadToGridFS(@RequestBody NewAdminBO newAdminBO) throws Exception;
imooc-news-dev-service-files加入配置和配置类返回GridFSBucket对象
data:
mongodb:
uri: mongodb://root:root@192.168.44.128:27017
database: imooc-news
package com.imooc.files;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class GridFSConfig {
@Value("${spring.data.mongodb.database}")
private String mongodb;
@Bean
public GridFSBucket gridFSBucket(MongoClient mongoClient){
MongoDatabase mongoDatabase = mongoClient.getDatabase(mongodb);
GridFSBucket bucket = GridFSBuckets.create(mongoDatabase);
return bucket;
}
}
2.3 实现人脸入库GridFS
- FileUploaderController中实现uploadToGridFS方法。
@Override
public GraceJSONResult uploadToGridFS(NewAdminBO newAdminBO) throws Exception {
// 获取图片的base64字符串
String file64 = newAdminBO.getImg64();
// 将base64字符串转换为byte数组
byte[] bytes = new BASE64Decoder().decodeBuffer(file64.trim());
// 转换为输入流
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
// 上传到gridfs中
ObjectId fileId = gridFSBucket.uploadFromStream(newAdminBO.getUsername() + ".png", byteArrayInputStream);
// 获得文件在gridfs中的主键id
String fileIdStr = fileId.toString();
return GraceJSONResult.ok(fileIdStr);
}
- 启动admin和file服务测试验证。
2.4 查看admin人脸信息
- common加入工具类FileUtils, 实现输出流到浏览器图片展示的功能(人脸图片输出到浏览器)。
- 实现readInGridFS接口。从gridfs读取图片下载到本地,然后从本地输出到浏览器中展示。
@Override
public void readInGridFS(String faceId,
HttpServletRequest request,
HttpServletResponse response) throws Exception{
// 1. 判断参数
if (StringUtils.isBlank(faceId) || faceId.equalsIgnoreCase("null")){
GraceException.display(ResponseStatusEnum.FILE_NOT_EXIST_ERROR);
}
// 2. 从gridfs中读取
File adminFace = readGridFSByFacerId(faceId);
// 3. 把人脸图片输出到浏览器
FileUtils.downloadFileByStream(response, adminFace);
}
private File readGridFSByFacerId(String faceId) throws Exception {
GridFSFindIterable gridFSFiles = gridFSBucket.find(Filters.eq("_id", new ObjectId(faceId)));
GridFSFile gridFS = gridFSFiles.first();
if (gridFS == null){
GraceException.display(ResponseStatusEnum.FILE_NOT_EXIST_ERROR);
}
String fileName = gridFS.getFilename();
System.out.println(gridFS.getFilename());
// 获取文件流,保存文件到本地或者服务器的临时目录
File fileTemp = new File("E:\\temp_face");
if (!fileTemp.exists()){
fileTemp.mkdirs();
}
File myFile = new File("E:\\temp_face\\" + fileName);
// 创建文件输出流
OutputStream os = new FileOutputStream(myFile);
// 下载到服务器或者本地
gridFSBucket.downloadToStream(new ObjectId(faceId), os);
return myFile;
}
- 启动admin, user, file 服务测试。如果其他服务要引入mongodb的配置类。排除一下
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
2.5 人脸登陆功能
- 开通阿里云的人脸识别(Face Recognition)功能,我们用到的功能测试不收费的。
- common引入依赖和工具类。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-facebody</artifactId>
<version>1.2.26</version>
</dependency>
@Component
public class FaceVerifyUtils {
final static Logger logger = LoggerFactory.getLogger(FaceVerifyUtils.class);
@Autowired
private AliyunResource aliyunResource;
public boolean faceVerify(int type, String face1, String face2, float targetConfidence) {
DefaultProfile profile = DefaultProfile.getProfile("cn-shanghai", aliyunResource.getAccessKeyID(), aliyunResource.getAccessKeySecret());
IAcsClient client = new DefaultAcsClient(profile);
CompareFaceRequest request = new CompareFaceRequest();
request.setImageDataA(face1);
request.setImageDataB(face2);
request.setQualityScoreThreshold(targetConfidence);
try {
CompareFaceResponse response = client.getAcsResponse(request);
if ((float)response.getData().getConfidence() > targetConfidence){
return true;
};
} catch (ServerException e) {
GraceException.display(ADMIN_FACE_LOGIN_ERROR);
} catch (ClientException e) {
System.out.println("ErrCode:" + e.getErrCode());
System.out.println("ErrMsg:" + e.getErrMsg());
System.out.println("RequestId:" + e.getRequestId());
GraceException.display(ADMIN_FACE_LOGIN_ERROR);
}
return false;
}
/**
*
* 将图片转换为Base64
* 将base64编码字符串解码成img图片
* @param imgUrl
* @return
*/
public String getImgBase64(String imgUrl){
ByteArrayOutputStream data = new ByteArrayOutputStream();
try {
// 创建URL
URL url = new URL(imgUrl);
byte[] by = new byte[1024];
// 创建链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
InputStream is = conn.getInputStream();
// 将内容放到内存中
int len = -1;
while ((len = is.read(by)) != -1) {
data.write(by, 0, len);
}
is.close();
} catch (IOException e) {
e.printStackTrace();
}
// 对字节数组Base64编码
return Base64.encodeBase64String(data.toByteArray());
}
}
- 实现人脸登录的接口。
@Override
public GraceJSONResult adminFaceLogin(AdminLoginBO adminLoginBO,
HttpServletRequest request,
HttpServletResponse response) {
// 0. 判断用户名和人脸信息不能为空
if (StringUtils.isBlank(adminLoginBO.getUsername())) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_USERNAME_NULL_ERROR);
}
String tempFace64 = adminLoginBO.getImg64();
if (StringUtils.isBlank(tempFace64)) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_FACE_NULL_ERROR);
}
// 1. 从数据库中查询出faceId
AdminUser admin = adminUserService.queryAdminByUsername(adminLoginBO.getUsername());
String adminFaceId = admin.getFaceId();
if (StringUtils.isBlank(adminFaceId)) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_FACE_LOGIN_ERROR);
}
// 2. 请求文件服务,获得人脸数据的base64数据
String fileServerUrlExecute
= "http://files.imoocnews.com:8004/fs/readFace64InGridFS?faceId=" + adminFaceId;
ResponseEntity<GraceJSONResult> responseEntity
= restTemplate.getForEntity(fileServerUrlExecute, GraceJSONResult.class);
GraceJSONResult bodyResult = responseEntity.getBody();
String base64DB = ((String)bodyResult.getData()).replaceAll("[\r\n]","");;
// System.out.println(tempFace64);
// System.out.println(base64DB);
// 3. 调用阿里ai进行人脸对比识别,判断可信度,从而实现人脸登录
boolean result = faceVerifyUtils.faceVerify(FaceVerifyType.BASE64.type,
tempFace64,
base64DB,
60);
if (!result) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_FACE_LOGIN_ERROR);
}
// 4. admin登录后的数据设置,redis与cookie
doLoginSettings(admin, request, response);
return GraceJSONResult.ok();
}
- 启动user,admin和file模块登录测试。
第三节 友情链接的相关操作
3.1 自定义注解BO层认证
- model的pojo定义
SaveFriendLinkBO加入定义注解@CheckUrl在BO层验证是否是一个合格的url。
package com.imooc.pojo.bo;
import com.imooc.validate.CheckUrl;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class SaveFriendLinkBO {
private String id;
@NotBlank(message = "友情链接名不能为空")
private String linkName;
@NotBlank(message = "友情链接地址不能为空")
@CheckUrl
private String linkUrl;
@NotNull(message = "请选择保留或删除")
private Integer isDelete;
}
- model的validate中自定义注解。
com.imooc.validate.CheckUrl这里通过CheckUrlValidate.class去验证
package com.imooc.validate;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckUrlValidate.class)
public @interface CheckUrl {
String message() default "Url不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
- model的validate中自定义验证函数
com.imooc.validate.CheckUrlValidate
package com.imooc.validate;
import com.imooc.utils.UrlUtil;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class CheckUrlValidate implements ConstraintValidator<CheckUrl, String> {
@Override
public boolean isValid(String url, ConstraintValidatorContext context) {
return UrlUtil.verifyUrl(url.trim());
}
}
- common中自定义工具类验证url是否合法。
package com.imooc.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class UrlUtil {
/**
* 验证是否是URL
* @param url
* @return
*/
public static boolean verifyUrl(String url){
// URL验证规则
// String regEx ="[A-Za-z]+://[A-Za-z0-9-_]+\\\\.[A-Za-z0-9-_%&\\?\\/.=]+";
String regEx = "^([hH][tT]{2}[pP]:/*|[hH][tT]{2}[pP][sS]:/*|[fF][tT][pP]:/*)(([A-Za-z0-9-~]+).)+([A-Za-z0-9-~\\/])+(\\?{0,1}(([A-Za-z0-9-~]+\\={0,1})([A-Za-z0-9-~]*)\\&{0,1})*)$";
// 编译正则表达式
Pattern pattern = Pattern.compile(regEx);
// 忽略大小写的写法
// Pattern pat = Pattern.compile(regEx, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(url);
// 字符串是否与正则表达式相匹配
boolean rs = matcher.matches();
return rs;
}
public static void main(String[] args) {
boolean res =
verifyUrl("http://admin.imoocnews.com:9090/imooc-news/admin/friendLinks.html");
System.out.println(res);
}
}
3.2 友情链接MO和Repository
- 先引入springboot的mongodb的启动类。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
- 首先model下写一个与mongodb交互的对象
com.imooc.pojo.mo.FriendLinkMO放到mo文件夹下。@Document("FriendLink")document相当于表名是FriendLink
package com.imooc.pojo.mo;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import java.util.Date;
@Document("FriendLink")
public class FriendLinkMO {
@Id
private String id;
@Field("link_name")
private String linkName;
@Field("link_url")
private String linkUrl;
@Field("is_delete")
private Integer isDelete;
@Field("create_time")
private Date createTime;
@Field("update_time")
private Date updateTime;
}
- admin服务写一个
com.imooc.admin.repository.FriendLinkRepository类似mysql的mapper提供封装一个操作mongodb的api。
package com.imooc.admin.repository;
import com.imooc.pojo.mo.FriendLinkMO;
import org.springframework.data.mongodb.repository.MongoRepository;
import java.util.List;
public interface FriendLinkRepository extends MongoRepository<FriendLinkMO, String> {
public List<FriendLinkMO> getAllByIsDelete(Integer isDelete);
}
3.3 友情链接service层实现
- admin中定义
com.imooc.admin.service.FriendLinkService
package com.imooc.admin.service;
import com.imooc.pojo.AdminUser;
import com.imooc.pojo.bo.NewAdminBO;
import com.imooc.pojo.mo.FriendLinkMO;
import com.imooc.utils.PagedGridResult;
import java.util.List;
public interface FriendLinkService {
/**
* 新增或者更新友情链接
*/
public void saveOrUpdateFriendLink(FriendLinkMO friendLinkMO);
/**
* 查询友情链接
*/
public List<FriendLinkMO> queryAllFriendLinkList();
/**
* 删除友情链接
*/
public void delete(String linkId);
/**
* 首页查询友情链接
*/
public List<FriendLinkMO> queryPortalAllFriendLinkList();
}
com.imooc.admin.service.impl.FriendLinkServiceImpl实现FriendLinkService
package com.imooc.admin.service.impl;
import com.imooc.admin.repository.FriendLinkRepository;
import com.imooc.admin.service.FriendLinkService;
import com.imooc.enums.YesOrNo;
import com.imooc.pojo.mo.FriendLinkMO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class FriendLinkServiceImpl implements FriendLinkService {
@Autowired
private FriendLinkRepository friendLinkRepository;
@Override
public void saveOrUpdateFriendLink(FriendLinkMO friendLinkMO) {
friendLinkRepository.save(friendLinkMO);
}
@Override
public List<FriendLinkMO> queryAllFriendLinkList() {
return friendLinkRepository.findAll();
}
@Override
public void delete(String linkId) {
friendLinkRepository.deleteById(linkId);
}
@Override
public List<FriendLinkMO> queryPortalAllFriendLinkList() {
return friendLinkRepository.getAllByIsDelete(YesOrNo.NO.type);
}
}
3.4 友情链接contoller层实现
- api中定义api
package com.imooc.api.controller.admin;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.pojo.bo.AdminLoginBO;
import com.imooc.pojo.bo.NewAdminBO;
import com.imooc.pojo.bo.SaveFriendLinkBO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import javax.naming.Binding;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
@Api(value = "首页友情链接维护", tags = {"首页友情链接维护controller"})
@RequestMapping("friendLinkMng")
public interface FriendLinkControllerApi {
@ApiOperation(value = "新增或者修改友情链接", notes = "新增或者修改友情链接", httpMethod = "POST")
@PostMapping("/saveOrUpdateFriendLink")
public GraceJSONResult saveOrUpdateFriendLink(
@RequestBody @Valid SaveFriendLinkBO saveFriendLinkBO,
BindingResult result);
@ApiOperation(value = "查询友情链接列表", notes = "查询友情链接列表", httpMethod = "POST")
@PostMapping("/getFriendLinkList")
public GraceJSONResult getFriendLinkList();
@ApiOperation(value = "删除友情链接", notes = "删除友情链接", httpMethod = "POST")
@PostMapping("/delete")
public GraceJSONResult delete(@RequestParam String linkId);
@ApiOperation(value = "门户端查询友情链接列表", notes = "门户端查询友情链接列表", httpMethod = "GET")
@GetMapping("portal/list")
public GraceJSONResult queryPortalAllFriendLinkList();
}
- admin中
com.imooc.admin.controller.FriendLinkController实现上面api
package com.imooc.admin.controller;
import com.imooc.admin.service.FriendLinkService;
import com.imooc.api.BaseController;
import com.imooc.api.controller.admin.FriendLinkControllerApi;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.pojo.bo.SaveFriendLinkBO;
import com.imooc.pojo.mo.FriendLinkMO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.Date;
import java.util.List;
import java.util.Map;
@RestController
public class FriendLinkController extends BaseController implements FriendLinkControllerApi {
final static Logger logger = LoggerFactory.getLogger(FriendLinkController.class);
@Autowired
private FriendLinkService friendLinkService;
@Override
public GraceJSONResult saveOrUpdateFriendLink(
@Valid SaveFriendLinkBO saveFriendLinkBO,
BindingResult result) {
if (result.hasErrors()) {
Map<String, String> map = getErrors(result);
return GraceJSONResult.errorMap(map);
}
// saveFriendLinkBO -> ***MO
FriendLinkMO saveFriendLinkMO = new FriendLinkMO();
BeanUtils.copyProperties(saveFriendLinkBO, saveFriendLinkMO);
saveFriendLinkMO.setCreateTime(new Date());
saveFriendLinkMO.setUpdateTime(new Date());
friendLinkService.saveOrUpdateFriendLink(saveFriendLinkMO);
return GraceJSONResult.ok();
}
@Override
public GraceJSONResult getFriendLinkList() {
return GraceJSONResult.ok(friendLinkService.queryAllFriendLinkList());
}
@Override
public GraceJSONResult delete(String linkId) {
friendLinkService.delete(linkId);
return GraceJSONResult.ok();
}
@Override
public GraceJSONResult queryPortalAllFriendLinkList() {
List<FriendLinkMO> list = friendLinkService.queryPortalAllFriendLinkList();
return GraceJSONResult.ok(list);
}
}
第四节 用户列表和冻结解封功能
- 首先,api中 把用户列表归到user的路由下。
package com.imooc.api.controller.user;
import com.imooc.grace.result.GraceJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Date;
@Api(value = "用户管理相关的接口定义", tags = {"用户管理相关功能的controller"})
@RequestMapping("appUser")
public interface AppUserMngControllerApi {
@PostMapping("queryAll")
@ApiOperation(value = "查询所有网站用户", notes = "查询所有网站用户", httpMethod = "POST")
public GraceJSONResult queryAll(@RequestParam String nickname,
@RequestParam Integer status,
@RequestParam Date startDate,
@RequestParam Date endDate,
@RequestParam Integer page,
@RequestParam Integer pageSize);
@PostMapping("userDetail")
@ApiOperation(value = "查看用户详情", notes = "查看用户详情", httpMethod = "POST")
public GraceJSONResult userDetail(@RequestParam String userId);
@PostMapping("freezeUserOrNot")
@ApiOperation(value = "冻结用户或者解冻用户", notes = "冻结用户或者解冻用户", httpMethod = "POST")
public GraceJSONResult freezeUserOrNot(@RequestParam String userId,
@RequestParam Integer doStatus);
@GetMapping("queryByIds")
@ApiOperation(value = "根据用户id查询用户", notes = "根据用户id查询用户", httpMethod = "GET")
public GraceJSONResult queryAll(@RequestParam String userIds);
}
- 这里需要在API中添加工具类DateConverterConfig,注入容器将前端传递过来的符合格式的Date字符串转为Date对象。
- user服务下创建
AppUserMngService
package com.imooc.user.service;
import com.imooc.pojo.AppUser;
import com.imooc.pojo.bo.UpdateUserInfoBO;
import com.imooc.utils.PagedGridResult;
import java.util.Date;
public interface AppUserMngService {
/**
* 查询管理员列表
*/
public PagedGridResult queryAllUserList(String nickname,
Integer status,
Date startDate,
Date endDate,
Integer page,
Integer pageSize);
/**
* 冻结用户账号,或者解除冻结状态
*/
public void freezeUserOrNot(String userId, Integer doStatus);
}
- 实现这个
AppUserMngServiceImpl
package com.imooc.user.service.impl;
import com.github.pagehelper.PageHelper;
import com.imooc.api.service.BaseService;
import com.imooc.enums.Sex;
import com.imooc.enums.UserStatus;
import com.imooc.exception.GraceException;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.AppUser;
import com.imooc.pojo.bo.UpdateUserInfoBO;
import com.imooc.user.mapper.AppUserMapper;
import com.imooc.user.service.AppUserMngService;
import com.imooc.user.service.UserService;
import com.imooc.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.n3r.idworker.Sid;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.util.Date;
import java.util.List;
@Service
public class AppUserMngServiceImpl extends BaseService implements AppUserMngService {
@Autowired
public AppUserMapper appUserMapper;
@Override
public PagedGridResult queryAllUserList(String nickname,
Integer status,
Date startDate,
Date endDate,
Integer page,
Integer pageSize) {
Example example = new Example(AppUser.class);
example.orderBy("createdTime").desc();
Example.Criteria criteria = example.createCriteria();
if (StringUtils.isNotBlank(nickname)) {
criteria.andLike("nickname", "%" + nickname + "%");
}
if (UserStatus.isUserStatusValid(status)) {
criteria.andEqualTo("activeStatus", status);
}
if (startDate != null) {
criteria.andGreaterThanOrEqualTo("createdTime", startDate);
}
if (endDate != null) {
criteria.andLessThanOrEqualTo("createdTime", endDate);
}
PageHelper.startPage(page, pageSize);
List<AppUser> list = appUserMapper.selectByExample(example);
return setterPagedGrid(list, page);
}
@Transactional
@Override
public void freezeUserOrNot(String userId, Integer doStatus) {
AppUser user = new AppUser();
user.setId(userId);
user.setActiveStatus(doStatus);
appUserMapper.updateByPrimaryKeySelective(user);
}
}
- Api中
com.imooc.api.service.BaseService,只要涉及分页的service都继承它就可以调用分页函数。
package com.imooc.api.service;
import com.github.pagehelper.PageInfo;
import com.imooc.utils.PagedGridResult;
import com.imooc.utils.RedisOperator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;
public class BaseService {
public static final String REDIS_ALL_CATEGORY = "redis_all_category";
public static final String REDIS_WRITER_FANS_COUNTS = "redis_writer_fans_counts";
public static final String REDIS_MY_FOLLOW_COUNTS = "redis_my_follow_counts";
public static final String REDIS_ARTICLE_COMMENT_COUNTS = "redis_article_comment_counts";
@Autowired
public RedisOperator redis;
public PagedGridResult setterPagedGrid(List<?> list,
Integer page) {
PageInfo<?> pageList = new PageInfo<>(list);
PagedGridResult gridResult = new PagedGridResult();
gridResult.setRows(list);
gridResult.setPage(page);
gridResult.setRecords(pageList.getTotal());
gridResult.setTotal(pageList.getPages());
return gridResult;
}
}
com.imooc.user.controller.AppUserMngController
package com.imooc.user.controller;
import com.imooc.api.BaseController;
import com.imooc.api.controller.user.AppUserMngControllerApi;
import com.imooc.api.controller.user.HelloControllerApi;
import com.imooc.enums.UserStatus;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.AppUser;
import com.imooc.pojo.vo.PublisherVO;
import com.imooc.user.service.AppUserMngService;
import com.imooc.user.service.UserService;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.PagedGridResult;
import com.imooc.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.List;
@RestController
public class AppUserMngController extends BaseController implements AppUserMngControllerApi {
final static Logger logger = LoggerFactory.getLogger(AppUserMngController.class);
@Autowired
private AppUserMngService appUserMngService;
@Autowired
private UserService userService;
@Override
public GraceJSONResult queryAll(String nickname,
Integer status,
Date startDate,
Date endDate,
Integer page,
Integer pageSize) {
// 这里Date通过api服务中的DateConverterConfig自动配置的配置类转化
// System.out.println(startDate);
// System.out.println(endDate);
if (page == null) {
page = COMMON_START_PAGE;
}
if (pageSize == null) {
pageSize = COMMON_PAGE_SIZE;
}
PagedGridResult result = appUserMngService.queryAllUserList(nickname,
status,
startDate,
endDate,
page,
pageSize);
return GraceJSONResult.ok(result);
}
@Override
public GraceJSONResult userDetail(String userId) {
return GraceJSONResult.ok(userService.getUser(userId));
}
@Override
public GraceJSONResult freezeUserOrNot(String userId, Integer doStatus) {
if (!UserStatus.isUserStatusValid(doStatus)) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_STATUS_ERROR);
}
appUserMngService.freezeUserOrNot(userId, doStatus);
// 刷新用户状态:
// 1. 删除用户会话,从而保障用户需要重新登录以后再来刷新他的会话状态
// 2. 查询最新用户的信息,重新放入redis中,做一次更新
redis.del(REDIS_USER_INFO + ":" + userId);
return GraceJSONResult.ok();
}
@Override
public GraceJSONResult queryAll(String userIds) {
if (StringUtils.isBlank(userIds)) {
return GraceJSONResult.errorCustom(ResponseStatusEnum.USER_NOT_EXIST_ERROR);
}
List<String> userIdList = JsonUtils.jsonToList(userIds, String.class);
// System.out.println(userIdList);
List<PublisherVO> userList = userService.getUserList(userIdList);
return GraceJSONResult.ok(userList);
}
}
该博客详细介绍了使用Spring Cloud开发微服务的实战经验,包括admin管理服务的构建,如用户登录验证、账号管理、人脸识别登录等功能。此外,还涵盖了与MongoDB的集成,实现文件上传到GridFS,以及友情链接的增删查改操作,包括自定义URL验证注解。最后,讲解了用户列表的分页查询和冻结解封功能的实现。

被折叠的 条评论
为什么被折叠?



