目录
项目模块
技术栈
功能展示
环境搭建
前端环境搭建:
后端环境搭建:
数据库创建:
(建议使用数据库软件操作)
springboot项目搭建:
mybatis-plus逆向工程
后端功能开发
用户模块开发
jwt 与 token
登录功能实现
根据token获取用户信息
注册用户名检查
用户注册功能
首页模块开发
查询首页分类
分页查询首页头条信息
查询头条详情
头条模块开发
登录验证和保护
头条发布实现
修改头条回显
头条修改实现
删除头条功能
完整后端代码:https://pan.baidu.com/s/1sEsK4x6uM_o32qdsVTxagQ?pwd=d67f
apifox测试文件:https://pan.baidu.com/s/11liz1MxjYeitrsJbua3VFA?pwd=k8we
项目模块
- 用户功能
- 注册功能
- 登录功能
- jwt实现
- 头条新闻
- 新闻的分页浏览
- 通过标题关键字搜索新闻
- 查看新闻详情
- 新闻的修改和删除
技术栈
前端技术栈:
- ES6作为基础JS语法
- nodejs用于运行环境
- npm用于项目依赖管理工具
- vite用于项目的构建架工具
- Vue3用于项目数据的渲染框架
- Axios用于前后端数据的交互
- Router用于页面的跳转
- Pinia用于存储用户的数据
- LocalStorage作为用户校验token的存储手段
- Element-Plus提供组件
后端技术栈:
- JAVA作为开发语言,版本为JDK17
- Tomcat作为服务容器,版本为10.1.7
- Mysql8用于项目存储数据
- SpringMVC用于控制层实现前后端数据交互
- MyBatis-Plus用于实现数据的CURD
- Druid用于提供数据源的连接池
- SpringBoot作为项目基础架构
- MD5用于用户密码的加密
- Jwt用于token的生成和校验
- Jackson用于转换JSON
功能展示
发布头条功能
修改头条功能
环境搭建
前端环境搭建:
前端环境下载https://pan.baidu.com/s/16HJROtpdTZ6DrL0GMNJncw?pwd=42wf
下载后解压,使用vscode打开文件夹,在vscode中打开终端依次输入:
npm install
npm run dev
后项目启动:
后端环境搭建:
数据库创建:
(建议使用数据库软件操作)
https://pan.baidu.com/s/19vKtJCn_CwyFHPwinpcmsA?pwd=fkky
springboot项目搭建:
1.创建boot工程:springboot-news
2.导入依赖
4.0.0 org.springframework.boot spring-boot-starter-parent3.0.5 com.qiu springboot-news1.0-SNAPSHOT org.springframework.boot spring-boot-starter-webcom.baomidou mybatis-plus-boot-starter3.5.3.1 org.springframework.boot spring-boot-starter-jdbccom.alibaba druid-spring-boot-3-starter1.2.18 mysql mysql-connector-java8.0.28 org.projectlombok lombok1.18.28 org.springframework.boot spring-boot-starter-aoporg.springframework.boot spring-boot-starter-testtest org.springframework.boot spring-boot-maven-plugin3.编写配置
# server配置 server: port: 8080 servlet: context-path: / # 连接池配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: url: jdbc:mysql:///sm_db username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # mybatis-plus的配置 mybatis-plus: type-aliases-package: com.qiu.pojo global-config: db-config: logic-delete-field: isDeleted #全局逻辑删除 id-type: auto #主键策略自增长 table-prefix: news_ # 设置表的前缀
4.解决druid问题
druid版本在1.2.20以下的需要:创建文件,添加内容
文件名:org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容:com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure
5.编写启动类和mybatis-plus插件
包:com.qiu
@SpringBootApplication @MapperScan("com.qiu.mapper") public class Main { public static void main(String[] args) { System.out.println("Hello world!"); SpringApplication.run(Main.class,args); } @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //分页 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //乐观锁 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); //防止全局修改和删除 interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } }
6.编写工具类
包:com.qiu.utils
结果封装类
/** * 全局统一返回结果类 */ public class Result
{ // 返回码 private Integer code; // 返回消息 private String message; // 返回数据 private T data; public Result(){} // 返回数据 protected static Result build(T data) { Result result = new Result (); if (data != null) result.setData(data); return result; } public static Result build(T body, Integer code, String message) { Result result = build(body); result.setCode(code); result.setMessage(message); return result; } public static Result build(T body, ResultCodeEnum resultCodeEnum) { Result result = build(body); result.setCode(resultCodeEnum.getCode()); result.setMessage(resultCodeEnum.getMessage()); return result; } /** * 操作成功 * @param data baseCategory1List * @param * @return */ public static Result ok(T data){ Result result = build(data); return build(data, ResultCodeEnum.SUCCESS); } public Result message(String msg){ this.setMessage(msg); return this; } public Result code(Integer code){ this.setCode(code); return this; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } } 结果状态信息枚举类
/** * 统一返回结果状态信息类 * */ public enum ResultCodeEnum { SUCCESS(200,"success"), USERNAME_ERROR(501,"usernameError"), PASSWORD_ERROR(503,"passwordError"), NOTLOGIN(504,"notLogin"), USERNAME_USED(505,"userNameUsed"); private Integer code; private String message; private ResultCodeEnum(Integer code, String message) { this.code = code; this.message = message; } public Integer getCode() { return code; } public String getMessage() { return message; } }
MD5加密工具类
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @Component public final class MD5Util { public static String encrypt(String strSrc) { try { char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; byte[] bytes = strSrc.getBytes(); MessageDigest md = MessageDigest.getInstance("MD5"); md.update(bytes); bytes = md.digest(); int j = bytes.length; char[] chars = new char[j * 2]; int k = 0; for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; chars[k++] = hexChars[b >>> 4 & 0xf]; chars[k++] = hexChars[b & 0xf]; } return new String(chars); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException("MD5加密出错!!+" + e); } } }
mybatis-plus逆向工程
1.逆向工程
2.完善pojo类
删除@TableName注解,全局统一设置
添加主键,乐观锁,逻辑删除注解
@Data public class User implements Serializable { @TableId private Integer uid; private String username; private String userPwd; private String nickName; @Version private Integer version; @TableLogic private Integer isDeleted; private static final long serialVersionUID = 1L; }
后端功能开发
用户模块开发
jwt 与 token
令牌(token):用于验证用户身份或授权用户对特定资源的访问。普通令牌可以以多种形式出现:访问令牌,身份令牌,刷新令牌等。
就是在用户登录后生成一段字符或数字给他,用户之后每次访问都携带这个token来证明自己的身份,这段字符或数字就是token
JWT介绍
Token是一项规范和标准(接口)
JWT(JSON Web Token)是具体可以生成,校验,解析等动作Token的技术(实现类)
JWT工作流程
- 用户通过其凭据(通常为账号密码)进行身份认证
- 服务器对凭据进行验证,验证成功后创建一个JWT
- 服务器将JWT发送给客户端,客户端在之后的请求中将JWT添加到请求头或参数中
- 服务器接收请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作
JWT的数据组成和包含信息
JWT由三部分组成: header(头部).payload(载荷).signature(签名)
jwt可以携带很多信息:有效时间,签名秘钥,其他用户标识信息等
有效时间为了保证token的时效性,过期可以重新登录获取
签名秘钥为了防止其他人随意解析和校验token数据
用户信息为了我们自己解析的时候,知道Token对应的具体用户
JWT的使用
1.导入依赖
io.jsonwebtoken jjwt0.9.1 javax.xml.bind jaxb-api2.3.0 2.编写配置
application.yaml
jwt: token: # tokenExpiration: 120 #有效时间,单位分钟 tokenExpiration: 1 #为了测试token过期,设置一分钟 tokenSignKey: qiu666 #当前程序签名秘钥 自定义
3.导入工具类
@Component @Data @ConfigurationProperties(prefix = "jwt.token") public class JwtHelper { //有效时间,单位毫秒 1000毫秒 == 1秒 private long tokenExpiration; //当前程序签名秘钥 private String tokenSignKey; //生成token public String createToken(Long userId){ String token = Jwts.builder().setSubject("YYGH-USER") // 1分钟 .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration * 1000 * 60)) //存放数据 .claim("userId", userId) //签名,当用户请求时携带token,根据签名判断身份是否通过 .signWith(SignatureAlgorithm.HS512, tokenSignKey) .compressWith(CompressionCodecs.GZIP).compact(); return token; } //当用户请求中携带token时,从token中获取数据(useId) public Long getUserId(String token){ //StringUtils的包为com.baomidou.mybatisplus.core.toolkit.StringUtils; if (StringUtils.isEmpty(token))return null; Jws
claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token); Claims claims = claimsJws.getBody(); Integer userId = (Integer) claims.get("userId"); return userId.longValue(); } //判断token是否有效 public boolean isExpiration(String token){ try { boolean isExpire = Jwts.parser() .setSigningKey(tokenSignKey) .parseClaimsJws(token) .getBody() .getExpiration().before(new Date()); //没有过期,有效,返回false return isExpire; }catch (Exception e){ //过期,返回true return true; } } } 4.使用和测试
@SpringBootTest public class JwtTest { @Autowired private JwtHelper jwtHelper; @Test public void test() throws InterruptedException { //生成数据 用户数据 userId 1L String token = jwtHelper.createToken(1L); System.out.println("token = " + token); //解析用户标识 Long userId = jwtHelper.getUserId(token); System.out.println("userId = " + userId); //查看token是否过期 false 未到期 true到期 boolean expiration = jwtHelper.isExpiration(token);; System.out.println("expiration = " + expiration); Thread.sleep(1000*60); System.out.println(" 程序睡眠一分钟后: "); //查看token是否过期 false 未到期 true到期 boolean expiration1 = jwtHelper.isExpiration(token);; System.out.println("expiration = " + expiration1); } }
登录功能实现
1.需求:用户在客户端输入用户名密码并向后端提交,后端根据用户名和密码判断登录是否成功,用户有误或者密码有误响应不同的提示信息!
2.接口描述:
url:user/login
请求方式:POST
请求参数:
{ "username":"zhangsan", "userPwd":"123456" }
响应数据:
成功
{ "code":"200", // 成功状态码 "message":"success" // 成功状态描述 "data":{ "token":"... ..." // 用户id的token } }
失败
{ "code":"501", "message":"用户名有误" "data":{} } 或者 { "code":"503", "message":"密码有误" "data":{} }
3.实现代码:
controller:
@RestController @RequestMapping("user") @CrossOrigin //解决跨域问题 public class UserController { @Autowired private UserService userService; @PostMapping("login") public Result
login(@RequestBody User user){ Result result = userService.login(user); return result; } } service:
@Service public class UserServiceImpl extends ServiceImpl
implements UserService{ @Autowired private JwtHelper jwtHelper; @Autowired private UserMapper userMapper; /** * 登录业务实现 * @param user * @return result封装 */ @Override public Result login(User user) { //根据账号查询 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getUsername,user.getUsername()); User loginUser = userMapper.selectOne(queryWrapper); //账号判断 if (loginUser == null) { //账号错误 return Result.build(null, ResultCodeEnum.USERNAME_ERROR); } //判断密码 if (!StringUtils.isEmpty(user.getUserPwd()) && loginUser.getUserPwd().equals(MD5Util.encrypt(user.getUserPwd()))) { //账号密码正确 //根据用户唯一标识生成token String token = jwtHelper.createToken(Long.valueOf(loginUser.getUid())); Map data = new HashMap(); data.put("token",token); return Result.ok(data); } //密码错误 return Result.build(null,ResultCodeEnum.PASSWORD_ERROR); } } 测试结果
根据token获取用户信息
1.需求:客户端发送请求,提交token请求头,后端根据token请求头获取登录用户的详细信息并响应给客户端进行存储
2.接口描述:
url:user/getUserInfo
请求方式:GET
请求头:
token:token内容
响应数据:
成功:
{ "code": 200, "message": "success", "data": { "loginUser": { "uid": 1, "username": "zhangsan", "userPwd": "", "nickName": "张三" } } }
失败:
{ "code": 504, "message": "notLogin", "data": null }
代码实现:
controller:
/** * 地址: user/getUserInfo * 方式: get * 请求头: token = token内容 * 返回: * { * "code": 200, * "message": "success", * "data": { * "loginUser": { * "uid": 1, * "username": "zhangsan", * "userPwd": "", * "nickName": "张三" * } * } * } * * 大概流程: * 1.获取token,解析token对应的userId * 2.根据userId,查询用户数据 * 3.将用户数据的密码置空,并且把用户数据封装到结果中key = loginUser * 4.失败返回504 (本次先写到当前业务,后期提取到拦截器和全局异常处理器) */ @GetMapping("getUserInfo") public Result userInfo(@RequestHeader String token){ Result result = userService.getUserInfo(token); return result; }
service:
/** * 查询用户数据 * @param token * @return result封装 */ @Override public Result getUserInfo(String token) { //1.判定是否有效期 if (jwtHelper.isExpiration(token)) { //true过期,直接返回未登录 return Result.build(null,ResultCodeEnum.NOTLOGIN); } //2.获取token对应的用户 int userId = jwtHelper.getUserId(token).intValue(); //3.查询数据 User user = userMapper.selectById(userId); if (user != null) { user.setUserPwd(null); Map data = new HashMap(); data.put("loginUser",user); return Result.ok(data); } return Result.build(null,ResultCodeEnum.NOTLOGIN); }
注册用户名检查
1.需求:用户在注册时输入用户名时,立刻将用户名发送给后端,后端根据用户名查询用户名是否可用并做出响应
2.接口描述:
url:user/checkUserName
请求方式:POST
请求参数:param形式
username=zhangsan
响应数据:
成功:
{ "code":"200", "message":"success" "data":{} }
失败:
{ "code":"505", "message":"用户名占用" "data":{} }
3.代码实现:
controller:
/** * url地址:user/checkUserName * 请求方式:POST * 请求参数:param形式 * username=zhangsan * 响应数据: * { * "code":"200", * "message":"success" * "data":{} * } * * 实现步骤: * 1. 获取账号数据 * 2. 根据账号进行数据库查询 * 3. 结果封装 */ @PostMapping("checkUserName") public Result checkUserName(String username){ Result result = userService.checkUserName(username); return result; }
service:
/** * 检查账号是否可以注册 * * @param username 账号信息 * @return */ @Override public Result checkUserName(String username) { LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getUsername,username); User user = userMapper.selectOne(queryWrapper); if (user != null){ return Result.build(null,ResultCodeEnum.USERNAME_USED); } return Result.ok(null); } 测试结果:
用户注册功能
1.需求:客户端将新用户信息发送给服务端,服务端将新用户存入数据库,存入之前做用户名是否被占用校验,校验通过响应成功提示,否则响应失败提示
2.接口描述:
url:user/regist
请求方式:POST
请求参数:
{ "username":"zhangsan", "userPwd":"123456", "nickName":"张三" }
响应数据:
成功:
{ "code":"200", "message":"success" "data":{} }
失败:
{ "code":"505", "message":"用户名占用" "data":{} }
3.代码实现:
controller:
/** * url地址:user/regist * 请求方式:POST * 请求参数: * { * "username":"zhangsan", * "userPwd":"123456", * "nickName":"张三" * } * 响应数据: * { * "code":"200", * "message":"success" * "data":{} * } * * 实现步骤: * 1. 将密码加密 * 2. 将数据插入 * 3. 判断结果,成 返回200 失败 505 */ @PostMapping("regist") public Result regist(@RequestBody User user){ Result result = userService.regist(user); return result; }
service:
@Override public Result regist(User user) { LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getUsername,user.getUsername()); Long count = userMapper.selectCount(queryWrapper); if (count > 0){ return Result.build(null,ResultCodeEnum.USERNAME_USED); } user.setUserPwd(MD5Util.encrypt(user.getUserPwd())); int rows = userMapper.insert(user); System.out.println("rows = " + rows); return Result.ok(null); } 测试结果:
首页模块开发
查询首页分类
1.需求:
进入新闻首页,查询所有分类并动态展示新闻类别栏位
2.接口描述:
url:portal/findAllTypes
请求方式:GET
请求参数:无
响应数据:
成功:
{ "code":"200", "message":"OK" "data":{ [ { "tid":"1", "tname":"新闻" }, { "tid":"2", "tname":"体育" }, { "tid":"3", "tname":"娱乐" }, { "tid":"4", "tname":"科技" }, { "tid":"5", "tname":"其他" } ] } }
3.代码实现
controller:
@RestController @RequestMapping("portal") @CrossOrigin public class PortalController { @Autowired private TypeService typeService; /** * 查询全部类别信息 * @return */ @GetMapping("findAllTypes") public Result findAllTypes(){ //直接调用业务层,查询全部数据 List
list = typeService.list(); return Result.ok(list); } } 分页查询首页头条信息
1.需求:
客户端向服务端发送查询关键字,新闻类别,页码数,页大小
服务端根据条件搜索分页信息,返回含页码数,页大小,总页数,总记录数,当前页数据等信息,并根据时间降序,浏览量降序排序
2.接口描述:
url:portal/findNewsPage
请求方式:POST
请求参数:
{ "keyWords":"马斯克", // 搜索标题关键字 "type":0, // 新闻类型 "pageNum":1, // 页码数 "pageSize":10 // 页大小 }
响应数据:
成功:
{ "code":"200", "message":"success" "data":{ "pageInfo":{ "pageData":[ { "hid":"1", // 新闻id "title":"尚硅谷宣布 ... ...", // 新闻标题 "type":"1", // 新闻所属类别编号 "pageViews":"40", // 新闻浏览量 "pastHours":"3" , // 发布时间已过小时数 "publisher":"1" // 发布用户ID }, { "hid":"1", // 新闻id "title":"尚硅谷宣布 ... ...", // 新闻标题 "type":"1", // 新闻所属类别编号 "pageViews":"40", // 新闻浏览量 "pastHours":"3", // 发布时间已过小时数 "publisher":"1" // 发布用户ID }, { "hid":"1", // 新闻id "title":"尚硅谷宣布 ... ...", // 新闻标题 "type":"1", // 新闻所属类别编号 "pageViews":"40", // 新闻浏览量 "pastHours":"3", // 发布时间已过小时数 "publisher":"1" // 发布用户ID } ], "pageNum":1, //页码数 "pageSize":10, // 页大小 "totalPage":20, // 总页数 "totalSize":200 // 总记录数 } } }
3.代码实现:
准备接收条件类:
@Data public class PortalVo { private String keyWords; private Integer type; private Integer pageNum = 1; private Integer pageSize =10; }
controller:
/** * 首页分页查询 * @return */ @PostMapping("findNewPages") public Result findNewPage(@RequestBody PortalVo portalVo){ Result result = headlineService.findNewPage(portalVo); return result; }
service:
@Service public class HeadlineServiceImpl extends ServiceImpl
implements HeadlineService{ @Autowired private HeadlineMapper headlineMapper; /** * 首页数据查询 * @param portalVo * @return */ @Override public Result findNewPage(PortalVo portalVo) { //1.条件拼接 需要非空判断 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.like(!StringUtils.isEmpty(portalVo.getKeyWords()),Headline::getTitle,portalVo.getKeyWords()) .eq(portalVo.getType()!= null,Headline::getType,portalVo.getType()); //2.分页参数 IPage page = new Page<>(portalVo.getPageNum(),portalVo.getPageSize()); //3.分页查询 //查询的结果 "pastHours":"3" // 发布时间已过小时数 我们查询返回一个map //自定义方法 headlineMapper.selectPageMap(page, portalVo); //4.结果封装 //分页数据封装 Map pageInfo =new HashMap<>(); pageInfo.put("pageData",page.getRecords()); pageInfo.put("pageNum",page.getCurrent()); pageInfo.put("pageSize",page.getSize()); pageInfo.put("totalPage",page.getPages()); pageInfo.put("totalSize",page.getTotal()); Map pageInfoMap=new HashMap<>(); pageInfoMap.put("pageInfo",pageInfo); // 响应JSON return Result.ok(pageInfoMap); } } mapper:
接口:
public interface HeadlineMapper extends BaseMapper
{ //自定义分页查询方法 IPage mapper.xml:
测试结果:
查询头条详情
1.需求:
用户点击"查看全文"时,向服务端发送新闻id
后端根据新闻id查询完整新闻文章信息并返回
后端要同时让新闻的浏览量+1
2.接口描述:
url:portal/showHeadlineDetial
请求方式:POST
请求参数: param形式
hid=1
响应数据:
成功:
{ "code":"200", "message":"success", "data":{ "headline":{ "hid":"1", // 新闻id "title":"马斯克宣布 ... ...", // 新闻标题 "article":"... ..." // 新闻正文 "type":"1", // 新闻所属类别编号 "typeName":"科技", // 新闻所属类别 "pageViews":"40", // 新闻浏览量 "pastHours":"3" , // 发布时间已过小时数 "publisher":"1" , // 发布用户ID "author":"张三" // 新闻作者 } } }
3.代码实现:
controller:
/** * 首页详情接口 * @param hid * @return */ @PostMapping("showHeadlineDetail") public Result showHeadlineDetail(Integer hid){ Result result = headlineService.showHeadlineDetail(hid); return result; }
service:
/** * 详情数据查询 * "headline":{ * "hid":"1", // 新闻id * "title":"马斯克宣布 ... ...", // 新闻标题 * "article":"... ..." // 新闻正文 * "type":"1", // 新闻所属类别编号 * "typeName":"科技", // 新闻所属类别 * "pageViews":"40", // 新闻浏览量 * "pastHours":"3" , // 发布时间已过小时数 * "publisher":"1" , // 发布用户ID * "author":"张三" // 新闻作者 * } * 注意: 是多表查询 , 需要更新浏览量+1 * * @param hid * @return */ @Override public Result showHeadlineDetail(Integer hid) { //1.实现根据id的查询(多表 Map headLineDetail = headlineMapper.selectDetailMap(hid); //2.拼接头条对象(阅读量和version)进行数据更新 Headline headline = new Headline(); headline.setHid(hid); headline.setPageViews((Integer) headLineDetail.get("pageViews")+1); //阅读量+1 headline.setVersion((Integer) headLineDetail.get("version")); //设置版本 headlineMapper.updateById(headline); Map
pageInfoMap=new HashMap<>(); pageInfoMap.put("headline",headLineDetail); return Result.ok(pageInfoMap); } mapper:
接口:
/** * 分页查询头条详情 * @param hid * @return */ Map selectDetailMap(Integer hid);
mapper.xml:
测试结果:
头条模块开发
登录验证和保护
1.需求:
客户端在进入发布页前、发布新闻前、进入修改页前、修改前、删除新闻前先向服务端发送请求携带token请求头
后端接收token请求头后,校验用户登录是否过期并做响应
前端根据响应信息提示用户进入登录页还是进入正常业务页面
2.接口描述:
url:user/checkLogin
请求方式:GET
请求参数:无
请求头:token: 用户token
3.代码实现:
controller:(登录后检查)
@GetMapping("checkLogin") public Result checkLogin(@RequestHeader String token){ if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){ //没有传或者过期 未登录 return Result.build(null, ResultCodeEnum.NOTLOGIN); } return Result.ok(null); }
创建拦截器:
@Component public class LoginProtectInterceptor implements HandlerInterceptor { @Autowired private JwtHelper jwtHelper; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("token"); if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){ Result result = Result.build(null, ResultCodeEnum.NOTLOGIN); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(result); response.getWriter().print(json); //拦截 return false; }else{ //放行 return true; } } }
配置拦截器:(使用 /headline 开头都拦截)
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private LoginProtectInterceptor loginProtectInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginProtectInterceptor).addPathPatterns("/headline/**"); } }
测试结果:
头条发布实现
1.需求:
用户在客户端输入发布的新闻信息完毕后
发布前先请求后端的登录校验接口验证登录
登录通过则提交新闻信息
后端将新闻信息存入数据库
2.接口描述:
url:headline/publish
请求方式:POST
请求头:token:用户token
请求参数:
{ "title":"尚硅谷宣布 ... ...", // 文章标题 "article":"... ...", // 文章内容 "type":"1" // 文章类别 }
响应数据:
未登录:
{ "code":"504", "message":"loginExpired", "data":{} }
成功:
{ "code":"200", "message":"success", "data":{} }
3.代码实现:
controller:
/** * 实现步骤: * 1. token获取userId [无需校验,拦截器会校验] * 2. 封装headline数据 * 3. 插入数据即可 */ @PostMapping("publish") public Result publish(@RequestBody Headline headline,@RequestHeader String token){ int userId = jwtHelper.getUserId(token).intValue(); headline.setPublisher(userId); Result result = headlineService.publish(headline); return result; }
service:
/** * 发布数据 * @param headline * @return */ @Override public Result publish(Headline headline) { headline.setCreateTime(new Date()); headline.setUpdateTime(new Date()); headline.setPageViews(0); headlineMapper.insert(headline); return Result.ok(null); }
测试结果:
修改头条回显
1.需求:
前端先调用登录校验接口,校验登录是否过期
登录校验通过后 ,则根据新闻id查询新闻的完整信息并响应给前端
2.接口描述:
url:headline/findHeadlineByHid
请求方式:POST
请求参数:param形式
hid=1
响应数据:
成功:
{ "code":"200", "message":"success", "data":{ "headline":{ "hid":"1", "title":"马斯克宣布", "article":"... ... ", "type":"2" } } }
3.代码实现:
controller:
@PostMapping("findHeadlineByHid") public Result findHeadlineByHid(Integer hid){ Result result = headlineService.findHeadlineByHid(hid); return result; }
service:
/** * 根据id查询详情 * @param hid * @return */ @Override public Result findHeadlineByHid(Integer hid) { Headline headline = headlineMapper.selectById(hid); Map
pageInfoMap=new HashMap<>(); pageInfoMap.put("headline",headline); return Result.ok(pageInfoMap); } 测试结果:
头条修改实现
1.需求:
客户端将新闻信息修改后,提交前先请求登录校验接口校验登录状态
登录校验通过则提交修改后的新闻信息,后端接收并更新进入数据库
2.接口描述:
url:headline/update
请求方式:POST
请求参数:
{ "hid":"1", "title":"尚硅谷宣布 ... ...", "article":"... ...", "type":"2" }
响应数据:
成功:
{ "code":"200", "message":"success", "data":{} }
3.代码实现:
controller:
@PostMapping("update") public Result update(@RequestBody Headline headline){ Result result = headlineService.updateHeadLine(headline); return result; }
service:
/** * 修改业务 * 1.查询version版本 * 2.补全属性,修改时间 , 版本! * * @param headline * @return */ @Override public Result updateHeadLine(Headline headline) { //读取版本 Integer version = headlineMapper.selectById(headline.getHid()).getVersion(); headline.setVersion(version); headline.setUpdateTime(new Date()); headlineMapper.updateById(headline); return Result.ok(null); }
测试结果:
删除头条功能
1.需求:
将要删除的新闻id发送给服务端
服务端校验登录是否过期,未过期则直接删除,过期则响应登录过期信息
2.接口描述:
url:headline/removeByHid
请求方式:POST
请求参数:param形式
hid=1
响应数据:
成功:
{ "code":"200", "message":"success", "data":{} }
3.代码实现:
controller:
@PostMapping("removeByHid") public Result removeById(Integer hid){ headlineService.removeById(hid); return Result.ok(null); }
测试结果:
还没有评论,来说两句吧...