Token
基本详情
令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。普通的令牌可能以各种形式出现,如访问令牌、身份令牌、刷新令牌等。
简单理解 : 每个用户生成的唯一字符串标识,可以进行用户识别和校验
类似技术: 天王盖地虎 ,小鸡炖蘑菇
优势: token验证标识无法直接识别用户的信息,盗取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依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
application.yaml
#jwt配置
jwt:
token:
tokenExpiration: 120 #有效时间,单位分钟
tokenSignKey: headline123456 #当前程序签名秘钥 自定义
封装jwt技术工具类(需要的时候复制)
package com.atguigu.utils;
import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Date;
@Data
@Component
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {
private long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒
private String tokenSignKey; //当前程序签名秘钥
//生成token字符串
public String createToken(Long userId) {
System.out.println("tokenExpiration = " + tokenExpiration);
System.out.println("tokenSignKey = " + tokenSignKey);
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟
.claim("userId", userId)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//从token字符串获取userid
public Long getUserId(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws<Claims> 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;
}
}
}
测试
flase是未过期
true是过期
@Test
public void testToken() {
//生成 传入用户标识
String token = jwtHelper.createToken(1L);
System.out.println("token 加密后的密钥= " + token);
//解析用户标识
int userId = jwtHelper.getUserId(token).intValue();
System.out.println("userId 解析密钥得到的 = " + userId);
//校验是否到期! false 未到期 true到期
boolean expiration = jwtHelper.isExpiration(token);
System.out.println("expiration是否过期 = " + expiration);
}
项目一:微头条
项目结构
数据库
news_headline表
news_type表
news_user表
步骤一:项目部署
1.1创建项目,并导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
</parent>
<groupId>org.example</groupId>
<artifactId>springboot-project2</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--web启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--快速启动-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<!--事务,jdbc等启动-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!--druid启动-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.20</version>
</dependency>
<!--mybatis plus 启动-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<!--打包-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.2编写配置文件
application-source.yml
#数据源
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sm_db?useUnicode=true
username: root
password: 123456
#mybatis plus
mybatis-plus:
configuration:
auto-mapping-unknown-column-behavior: failing #自动注入
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #日志输出
map-underscore-to-camel-case: true #驼峰
global-config:
db-config:
table-prefix: news_ # 配置MyBatis-Plus操作表的默认前缀
id-type: auto # 全局主键的自增策略
logic-delete-field: isDeleted #全局指定逻辑删除
logic-delete-value: 1 #逻辑已删除是1
logic-not-delete-value: 0 #逻辑未删除是0
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.example.pojo
application.yml
#导入
spring:
profiles:
active: source
#设置端口号的请求头
server:
port: 8080
servlet:
context-path: /
#jwt配置
jwt:
token:
tokenExpiration: 120 #有效时间,单位分钟
tokenSignKey: headline123456 #当前程序签名秘钥 自定义
1.3用 mybatis X 自动生成实体类,service层,mapper层
注意:给自动生成的实体类层的主键属性上加@TableId,版本号属性上加@Version
1.4启动类
@SpringBootApplication
@MapperScan("org.example.mapper")
public class Main {
public static void main(String[] args) {
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;
}
}
步骤二:工具类
json响应
package org.example.json;
public class Result <T>{
private Integer code;
private String message;
private T data;
public Result(){}
public static <T> Result<T>build(T body){
Result result = new Result();
result.setData(body);
return result;
}
public static <T> Result<T> build(T body,ResultEnum resultEnum){
Result<T> result = build(body);
result.setCode(resultEnum.getCode());
result.setMessage(resultEnum.getMessage());
return result;
}
public static <T> Result<T> ok(T body){
Result<T> result = build(body);
result.setCode(ResultEnum.SUCCESS.getCode());
result.setMessage(ResultEnum.SUCCESS.getMessage());
return result;
}
public int getCode() {
return code;
}
public void setCode(int 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;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
package org.example.json;
public enum ResultEnum {
SUCCESS(200, "成功"),
ERROR(500, "失败"),
PARAM_ERROR(400, "参数错误"),
NOT_FOUND(404, "未找到"),
NOT_LOGIN(401, "未登录"),
FORBIDDEN(403, "禁止访问"),
DUPLICATE_KEY(500, "数据库中已存在该记录"),
NOT_AUTH(403, "没有权限"),
NOT_EXIST(404, "不存在"),
NOT_MATCH(403, "不匹配"),
NOT_PERMISSION(403, "没有权限"),
NOT_VALID(400, "无效"),
YHMCW(501, "用户名有误"),
MMYW(503, "密码有误"),
TOken(504,"notLogin"),
YHM(505,"用户名被占用"),
;
private Integer code;
private String message;
ResultEnum() {
}
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
MD5加密
public 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(); //使用平台的默认字符集将此 String 编码为 byte 序列,并 将结果存储到一个新的 byte 数组中。
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加密出错");
}
}
}
token令牌
这里给它加入到ioc容器中,方便引入前面我们在配置文件中给token定义的有效时间和签名密钥。
后续通过@Autowired来使用
不用@Autowired,用new的话,配置文件给他定义的就不生效了
@Test public void test2() { JwtHelper jwtHelper = new JwtHelper(); System.out.println("有效时间"+jwtHelper.getTokenExpiration()); System.out.println("当前签名密码"+jwtHelper.getTokenSignKey()); }
@Data
@Component
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {
private long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒
private String tokenSignKey; //当前程序签名秘钥
//生成token字符串
public String createToken(Long userId) {
System.out.println("tokenExpiration = " + tokenExpiration);
System.out.println("tokenSignKey = " + tokenSignKey);
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟
.claim("userId", userId)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//从token字符串获取userid
public Long getUserId(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws<Claims> 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;
}
}
}
步骤三:后台开发
用户模块开发
用户在客户端输入用户名密码并向后端提交,后端根据用户名和密码判断登录是否成功,用户有误或者密码有误响应不同的提示信息!
controller
/*获取token*/
@PostMapping("login")
public Result login(@RequestBody User user){
Result result = userService.login(user);
System.out.println(result);
return result;
}
service
@Override
public Result login(User user) {
Result result = null;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
//设置查询条件, 这里设置的是名字相同,因为密码前端传过来的是明文密码,所以这里先不做比较
wrapper.eq(User::getUsername, user.getUsername());
User user1 = userMapper.selectOne(wrapper);
if (user1 == null) {
result = Result.build(null, ResultEnum.YHMCW);
} else {
if (!(MD5Util.encrypt(user.getUserPwd()).equals(user1.getUserPwd()))) {
result = Result.build(null, ResultEnum.MMYW);
} else {
String token = jwtHelper.createToken(Long.valueOf(user1.getUid()));
HashMap<String, String> map = new HashMap<>();
map.put("token", token);
result = Result.ok(map);
}
}
return result;
}
客户端发送请求,提交token请求头,后端根据token请求头获取登录用户的详细信息并响应给客户端进行存储
controller
/*登入*/
@GetMapping("getUserInfo")
public Result getUserInfo(@RequestHeader String token){
Result userInfo = userService.getUserInfo(token);
return userInfo;
}
service
@Override
public Result getUserInfo(String token) {
Result result;
if (jwtHelper.isExpiration(token)) {
//true是过期
return result = Result.build(null, ResultEnum.TOken);
}
int id = jwtHelper.getUserId(token).intValue();
User user = userMapper.selectById(id);
if (user == null) {
return result = Result.build(null, ResultEnum.TOken);
}
user.setUserPwd("");
HashMap<String, User> map = new HashMap<>();
map.put("loginUser", user);
result = Result.ok(map);
return result;
}
用户在注册时输入用户名时,立刻将用户名发送给后端,后端根据用户名查询用户名是否可用并做出响应
controller
//检查用户名是否可用/**/
@PostMapping("checkUserName")
public Result checkUserName(@RequestParam("username") String name){
Result result = userService.checkUserName(name);
return result;
}
service
@Override
public Result checkUserName(String username) {
Result result=null;
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername,username);
User user = userMapper.selectOne(wrapper);
if(user!=null){
return Result.build(null,ResultEnum.YHM);
}else {
return Result.ok(null);
}
}
客户端将新用户信息发送给服务端,服务端将新用户存入数据库,存入之前做用户名是否被占用校验,校验通过响应成功提示,否则响应失败提示
controller
//注册
@PostMapping("regist")
public Result regist( @RequestBody User user){
Result regist = userService.regist(user);
return regist;
}
service
@Override
public Result regist(User user) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername,user.getUsername());
Long l = userMapper.selectCount(wrapper);
if(l>0){
return Result.build(null,ResultEnum.YHM);
}
//密码MD5加密
user.setUserPwd(MD5Util.encrypt(user.getUserPwd()));
int insert = userMapper.insert(user);
return Result.ok(null);
}
首页模块开发
进入新闻首页,查询所有分类并动态展示新闻类别栏位
controller
//进入新闻首页,查询所有分类并动态展示新闻类别栏位
@GetMapping("findAllTypes")
public Result findAllTypes(){
return typeService.findAllTypes();
}
service
@Override
public Result findAllTypes() {
List<Type> types = typeMapper.selectList(null);
return Result.ok(types);
}
- 客户端向服务端发送查询关键字,新闻类别,页码数,页大小
- 服务端根据条件搜索分页信息,返回含页码数,页大小,总页数,总记录数,当前页数据等信息,并根据时间降序,浏览量降序排序
因为请求体中的这几个数据,我们并没有来接收他们的类,所以这里可以定义一个用来接收客户端发送来的数据的类
@Data
public class PageVo {
private String keyWords;
private Integer type;
private Integer pageNum;
private Integer pageSize;
}
controller
/*分页查询首页头条信息*/
@PostMapping("findNewsPage")
public Result findNewsPage(@RequestBody PageVo pageVo){
Result result= headlineService.selectMyPage(pageVo);
return result;
}
service
@Override
public Result selectMyPage(PageVo pageVo) {
Result result = null;
// 分页查询
IPage<Headline> page = new Page<>(pageVo.getPageNum(), pageVo.getPageSize());
headlineMapper.selectMyPage(page, pageVo);
//分页数据封装
Map<String,Object> 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<String,Object> pageInfoMap=new HashMap<>();
pageInfoMap.put("pageInfo",pageInfo);
// 响应JSON
return Result.ok(pageInfoMap);
}
因为是多表联查, mybatis plus没有这样的方法,所以这个时候需要我们自己编写sql语句
mapper
IPage<Map> selectMyPage(IPage<Headline> page, @Param("portalVo")PageVo portalVo);
mapper.xml
<select id="selectMyPage" resultType="map">
select hid,title,type,page_views pageViews,TIMESTAMPDIFF(HOUR,create_time,NOW()) pastHours,
publisher from news_headline where is_deleted=0
<if test="portalVo.keyWords !=null and portalVo.keyWords.length()>0 ">
and title like concat('%',#{portalVo.keyWords},'%')
</if>
<if test="portalVo.type != null and portalVo.type != 0">
and type = #{portalVo.type}
</if>
</select>
- 用户点击"查看全文"时,向服务端发送新闻id
- 后端根据新闻id查询完整新闻文章信息并返回
- 后端要同时让新闻的浏览量+1
controller
@PostMapping("showHeadlineDetail")
public Result showHeadlineDetail(@RequestParam int hid){
return headlineService.showHeadlineDetail(hid);
}
service
@Override
public Result showHeadlineDetail(int hid) {
Map map = headlineMapper.showHeadlineDetail(hid);
Headline headline = new Headline();
headline.setHid(hid);
// headline.setVersion((Integer) map.get("version")); //设置版本 因每次修改都会更新版本
headline.setPageViews((Integer) map.get("pageViews")+1); //阅读量+1
headlineMapper.updateById(headline);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("headline", map);
return Result.ok(hashMap);
}
这里同样是多表,需要我们自己定义sql语句
mapper
Map showHeadlineDetail(@Param("hid") int hid);
mapper.xml
<select id="showHeadlineDetail" resultType="java.util.Map">
select hid,title,article,type,tname typeName,page_views pageViews, timestampdiff(hour ,create_time,now()) pastHours,
publisher,nick_name author from news_headline head left join news_type newst
on type=newst.tid left join news_user user on publisher=user.uid
where hid=#{hid}
</select>
头条模块开发
- 客户端在进入发布页前、发布新闻前、进入修改页前、修改前、删除新闻前先向服务端发送请求携带token请求头
- 后端接收token请求头后,校验用户登录是否过期并做响应
- 前端根据响应信息提示用户进入登录页还是进入正常业务页面
controller
//检查token是否过期
@GetMapping("checkLogin")
public Result checkLogin(@RequestHeader String token){
Result result = userService.checkLogin(token);
return result;
}
service
@Override
public Result checkLogin(String token) {
boolean tokem = jwtHelper.isExpiration(token);
if(tokem){
return Result.build(null,ResultEnum.TOken);
}
return Result.ok(null);
}
编写拦截器
【所有/headline开头都需要检查登陆】
@Component
public class HeadlineInterceptors implements HandlerInterceptor {
@Autowired
private JwtHelper jwtHelper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// System.out.println(jwtHelper);
// 获取请求头中的token
String token = request.getHeader("token");
// System.out.println(jwtHelper);
// System.out.println(token);
if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){
Result result = Result.build(null, ResultEnum.TOken);
ObjectMapper objectMapper = new ObjectMapper();
//转化成json形式
String json = objectMapper.writeValueAsString(result);
response.getWriter().print(json);
//拦截
System.out.println("拦截了--------------------------");
return false;
}else{
//放行
System.out.println("放行了--------------------------");
return true;
}
}
}
拦截器配置
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private HeadlineInterceptors HeadlineInterceptors;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(HeadlineInterceptors).addPathPatterns("/headline/*");//配置拦截器和拦截路径
}
}
- 用户在客户端输入发布的新闻信息完毕后
- 发布前先请求后端的登录校验接口验证登录
- 登录通过则提交新闻信息
- 后端将新闻信息存入数据库
controller
@PostMapping("publish")
public Result publish(@RequestHeader String token, @RequestBody Headline headline){
Result publish = headlineService.publish(token, headline);
return publish;
}
service
@Override
public Result publish(String token, Headline headline) {
int id = jwtHelper.getUserId(token).intValue();
headline.setPublisher(id);
headline.setPageViews(0);
headline.setCreateTime(new Date());
headline.setUpdateTime(new Date());
headlineMapper.insert(headline);
return Result.ok(null);
}
- 前端先调用登录校验接口,校验登录是否过期
- 登录校验通过后 ,则根据新闻id查询新闻的完整信息并响应给前端
controller
@PostMapping("findHeadlineByHid")
public Result findHeadlineByHid(@RequestParam int hid){
Result findHeadlineByHid = headlineService.findHeadlineByHid(hid);
return findHeadlineByHid;
}
service
@Override
public Result findHeadlineByHid(int hid) {
Headline headline = headlineMapper.selectById(hid);
HashMap<String, Object> map = new HashMap<>();
map.put("headline", headline);
return Result.ok(map);
}
- 客户端将新闻信息修改后,提交前先请求登录校验接口校验登录状态
- 登录校验通过则提交修改后的新闻信息,后端接收并更新进入数据库
controller
@PostMapping("update")
public Result update(@RequestBody Headline headline){
Result update = headlineService.update(headline);
return update;
}
service
@Override
public Result update(Headline headline) {
headline.setUpdateTime(new Date());
headlineMapper.updateById(headline);
return Result.ok(null);
}
- 将要删除的新闻id发送给服务端
- 服务端校验登录是否过期,未过期则直接删除,过期则响应登录过期信息
controller
@Override
public Result removeByHid(int hid) {
headlineMapper.deleteById(hid);
return Result.ok(null);
}
项目实战二(小项目)
还是那几个依赖,这里不演示了
配置文件也是那几个,参考上面
数据库
CREATE TABLE schedule (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
completed BOOLEAN NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO schedule (title, completed)
VALUES
('学习java', true),
('学习Python', false),
('学习C++', true),
('学习JavaScript', false),
('学习HTML5', true),
('学习CSS3', false),
('学习Vue.js', true),
('学习React', false),
('学习Angular', true),
('学习Node.js', false),
('学习Express', true),
('学习Koa', false),
('学习MongoDB', true),
('学习MySQL', false),
('学习Redis', true),
('学习Git', false),
('学习Docker', true),
('学习Kubernetes', false),
('学习AWS', true),
('学习Azure', false);
/*
需求说明
查询全部数据页数据
请求uri
schedule/{pageSize}/{currentPage}
请求方式
get
响应的json
{
"code":200,
"flag":true,
"data":{
//本页数据
data:
[
{id:1,title:'学习java',completed:true},
{id:2,title:'学习html',completed:true},
{id:3,title:'学习css',completed:true},
{id:4,title:'学习js',completed:true},
{id:5,title:'学习vue',completed:true}
],
//分页参数
pageSize:5, // 每页数据条数 页大小
total:0 , // 总记录数
currentPage:1 // 当前页码
}
}
*/
controller
@GetMapping("/{pageSize}/{currentPage}")
public Result getSchedule(@PathVariable("pageSize") int pageSize, @PathVariable("currentPage") int currentPage) {
Result result = service.schedule(pageSize,currentPage);
return result;
}
service
@Override
public Result schedule(int pageSize, int currentPage) {
Page<Schedule> page = new Page<>(currentPage, pageSize);
scheduleMapper.selectPage(page, null);
HashMap<String, Object> map = new HashMap<>();
map.put("data",page.getRecords()); //获取数据
map.put("pageSize",page.getSize()); //获取每页大小
map.put("total",page.getTotal()); //获取总条数
map.put("currentPage",page.getCurrent()); //获取页面
return Result.ok(map);
}
/*
需求说明
根据id删除日程
请求uri
schedule/{id}
请求方式
delete
响应的json
{
"code":200,
"flag":true,
"data":null
}
*/
controller
@DeleteMapping("/{id}")
public Result getScheduleById(@PathVariable("id") int id) {
Result result = service.deleteSchedule(id);
return result;
}
service
@Override
public Result deleteSchedule(int id) {
int i = scheduleMapper.deleteById(id);
if (i == 1) {
return Result.ok(null);
} else
return Result.fail(null);
}
/*
需求说明
增加日程
请求uri
schedule
请求方式
post
请求体中的JSON
{
title: '',
completed: false
}
响应的json
{
"code":200,
"flag":true,
"data":null
}
*/
controller
@PostMapping
public Result addSchedule(@RequestBody Schedule schedule) {
Result result = service.addSchedule(schedule);
return result;
}
service
@Override
public Result addSchedule(Schedule schedule) {
int i = scheduleMapper.insert(schedule);
if (i == 1) {
return Result.ok(null);
} else
return Result.fail(null);
}
/*
需求说明
根据id修改数据
请求uri
schedule
请求方式
put
请求体中的JSON
{
id: 1,
title: '',
completed: false
}
响应的json
{
"code":200,
"flag":true,
"data":null
}
*/
controller
@PutMapping
public Result updateSchedule(@RequestBody Schedule schedule) {
Result result = service.updateSchedule(schedule);
return result;
}
service
@Override
public Result updateSchedule(Schedule schedule) {
int i = scheduleMapper.updateById(schedule);
if (i == 1) {
return Result.ok(null);
} else
return Result.fail(null);
}