1.實現Token校驗
## Token校驗URL```json
GET /checkToken
```參數```json
HttpServletRequest request
```返回```json
{"msg": "操作成功","code": 200,"status": "ok"
}{"msg": "操作成功","code": 200,"status": "error"
}
?在LoginController中實現下面得方法
/*** Token校驗* @param request HTTP請求* @return 校驗結果*/@RequestMapping("/checkToken")public Result checkToken(HttpServletRequest request) {//1.從請求頭中獲取tokenString token = request.getHeader("token");System.out.println("從請求頭獲取到的token:" + token);//2.判斷token是否為空或空字符串,如果是則返回錯誤信息if (token == null || token.isEmpty()) {System.out.println("token為空或空字符串,驗證失敗");return Result.ok().put("status", "error").put("msg", "token不能為空");}//3.聲明變量用于標記JWT格式是否有效boolean isJwtValid;//4.聲明變量用于存儲從token中解析出的userIdString userId = null;try {//5.使用JWT工具類解析token,獲取負載信息Claims claims = Jwts.parser().setSigningKey(jwtUtil.getSecret()).parseClaimsJws(token).getBody();//6.從負載中獲取userIduserId = claims.get("userId", String.class);System.out.println("從token中解析出的userId:" + userId);//7.如果解析成功,標記JWT格式有效isJwtValid = true;System.out.println("JWT格式驗證通過");} catch (Exception e) {//8.如果解析失敗,標記JWT格式無效isJwtValid = false;System.out.println("JWT格式驗證失敗,異常信息:" + e.getMessage());}//9.如果JWT格式無效,返回錯誤信息if (!isJwtValid) {System.out.println("JWT格式無效,返回錯誤響應");return Result.ok().put("status", "error").put("msg", "token格式無效");}//10.根據userId拼接Redis中存儲token的keyString redisKey = "communityuser-" + userId;System.out.println("Redis中存儲token的key:" + redisKey);//11.從Redis中獲取存儲的tokenString redisToken = (String) redisTemplate.opsForValue().get(redisKey);System.out.println("從Redis中獲取到的token:" + redisToken);//12.判斷Redis中是否存在token且與請求中的token一致boolean isTokenValid = redisToken != null && redisToken.equals(token);System.out.println("Redis中token與請求token是否一致:" + isTokenValid);//13.根據雙重驗證結果返回對應的響應信息if (isTokenValid) {System.out.println("token雙重驗證通過,返回成功響應");return Result.ok().put("status", "ok").put("msg", "token驗證通過");} else {System.out.println("token雙重驗證失敗,返回錯誤響應");return Result.ok().put("status", "error").put("msg", "token已失效或不匹配");}}
利用上面得Token校驗實現動態路由
加載動態路由
實現思路
1.通過session獲取用戶信息
2.根據userId獲取角色名稱,需要在user_role表和role表中聯表查詢
3.根據userId獲取用戶的權限菜單:
第一步:根據用戶的id查詢該用戶所對應的角色以及該角色所對應的菜單,需要user_role、user_menu、menu三個表聯表查詢;
第二步:按照查詢出來的菜單進行封裝,一個一級菜單的信息封裝進一個列表,此菜單下的二級菜單的信息封裝進此列表的子列表中,若有三級菜單以此類推進行封裝
④返回用戶信息、角色名稱和用戶的權限菜單信息,格式如
```json
{"msg": "操作成功","code": 200,"data": {"userId": 1,"username": "admin", "password": "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92","realName": "管理員", "contact": "", "mobile": "15679711120", "status": 1},"roles": "超級管理員","routers": [{"name": "系統管理","path": "/sys","hidden": "false","redirect": "noRedirect","component": "Layout","alwaysShow": true,"meta": {"title": "系統管理","icon": "system"},"children": [{"name": "管理員管理","path": "/user","hidden": "false","component": "sys/user/index","meta": {"title": "管理員管理","icon": "user"}}]}]
}
封裝返回的routers信息MenuRouterVO、ChildMenuRouterVO、MenuRouterVO
package com.qcby.vo;import lombok.Data;import java.util.List;@Data
public class MenuRouterVO {private String name;private String path;private String component;private String hidden;private String redirect = "noRedirect";private Boolean alwaysShow = true;private MetaVO meta;private List<ChildMenuRouterVO> children;}
package com.qcby.vo;import lombok.Data;@Data
public class ChildMenuRouterVO {private String name;private String path;private String component;private String hidden;private MetaVO meta;}
package com.qcby.vo;import lombok.Data;@Data
public class MetaVO {private String title;private String icon;
}
加載動態路由controller請求
/*** 通過登錄的用于加載動態路由* 顯示該用戶能訪問的菜單* @param session* @return*/@RequestMapping("/getRouters")public Result getRouters(HttpSession session){//獲取用戶名稱User user = (User)session.getAttribute("user");//獲取用戶的角色名稱String roles = roleMapper.getRoleNameByUserId(user.getUserId());//獲取用戶的權限菜單List<MenuRouterVO> routers = this.menuService.getMenuRouterByUserId(user.getUserId());return Result.ok().put("data", user).put("roles", roles).put("routers",routers);}
獲取用戶的角色名稱的mapper
@Select("SELECT role_name FROM role, user_role where user_role.role_id=role.role_id and user_role.user_id=#{userId}")public String getRoleNameByUserId(Integer userId);
獲取用戶的菜單信息service
/*** 重寫接口方法,根據用戶ID獲取菜單路由信息* @param userId 用戶ID* @return 菜單路由信息列表*/@Overridepublic List<MenuRouterVO> getMenuRouterByUserId(Integer userId) {// 1. 根據用戶ID查詢該用戶擁有的角色及對應的所有菜單列表List<Menu> menuList = this.menuMapper.getMenusByUserId(userId);// 2. 創建最終要返回的菜單路由VO集合(VO用于前端展示的數據模型)List<MenuRouterVO> list = new ArrayList<>();// 3. 遍歷所有菜單,篩選出一級菜單(父菜單ID為0)并封裝成MenuRouterVOfor (Menu menu : menuList) {// 篩選一級菜單(parentId為0表示是頂級菜單)if (menu.getParentId() == 0) {// 創建一級菜單路由VO對象MenuRouterVO menuRouterVO = new MenuRouterVO();// 復制Menu對象的屬性到MenuRouterVO(使用框架提供的屬性拷貝工具類)BeanUtils.copyProperties(menu, menuRouterVO);// 封裝Meta信息(前端顯示需要的標題和圖標)MetaVO metaVO = new MetaVO();metaVO.setTitle(menu.getName()); // 設置菜單標題metaVO.setIcon(menu.getIcon()); // 設置菜單圖標menuRouterVO.setMeta(metaVO); // 將meta信息設置到路由對象// 獲取當前一級菜單的ID,用于匹配其子菜單Integer menuId = menu.getMenuId();// 4. 創建子菜單集合,用于存儲當前一級菜單下的所有子菜單List<ChildMenuRouterVO> children = new ArrayList<>();// 遍歷所有菜單,篩選出屬于當前一級菜單的子菜單for (Menu child : menuList) {// 判斷當前菜單是否為當前一級菜單的子菜單(子菜單的parentId等于父菜單的menuId)if(child.getParentId() == menuId){// 5. 創建子菜單路由VO對象并封裝數據ChildMenuRouterVO childVO = new ChildMenuRouterVO();BeanUtils.copyProperties(child, childVO); // 復制基本屬性// 封裝子菜單的Meta信息MetaVO childMetaVO = new MetaVO();childMetaVO.setTitle(child.getName()); // 子菜單標題childMetaVO.setIcon(child.getIcon()); // 子菜單圖標childVO.setMeta(childMetaVO); // 設置子菜單的meta信息// 將子菜單添加到子菜單集合children.add(childVO);}}// 6. 將子菜單集合設置到當前一級菜單路由對象中menuRouterVO.setChildren(children);// 7. 將封裝好的一級菜單路由對象添加到最終返回的集合中list.add(menuRouterVO);}}// 返回整理好的菜單路由列表(包含一級菜單和對應的子菜單)return list;}
獲取用戶的菜單信息mapper
@Select({"select m.menu_id,m.parent_id,m.name,m.path,m.component," +"m.menu_type,m.status,m.icon,m.sort,m.hidden from " +"user_role ur,role_menu rm,menu m where ur.role_id = rm.role_id" +" and rm.menu_id = m.menu_id " +"and ur.user_id = #{userId} order by m.sort"})public List<Menu> getMenusByUserId(Integer userId);
結果圖
登錄進去之后就能夠看到一級目錄
每個目錄下面有對應得子目錄
2.修改密碼
實現思路
①發送更新密碼請求,彈出更新密碼彈出層
②前端密碼格式驗證,新舊密碼是否一致驗證
③修改密碼:第一步獲取session中的用戶信息;第二步將根據用戶查詢的密碼和前端傳來的舊密碼進行比較,如果相等,將新密碼加密后在數據庫中更新密碼字段信息,密碼更新成功,返回。
修改密碼請求處理
/*** 修改用戶密碼的控制器方法* @return 返回操作結果*/@PutMapping("/updatePassword") // 使用PUT映射處理/updatePassword請求public Result updatePassword(@RequestBody UpdatePasswordFrom updatePasswordFrom, HttpSession session){// 從session中獲取當前登錄的用戶信息User user = (User)session.getAttribute("user");// 獲取用戶當前存儲的密碼String pwd = user.getPassword();// 使用SHA-256算法對用戶輸入的原密碼進行加密String password = SecureUtil.sha256(updatePasswordFrom.getPassword());// 比較數據庫中的密碼和用戶輸入的原密碼是否一致if(pwd.equals(password)){String newpassword = SecureUtil.sha256(updatePasswordFrom.getNewPassword());user.setPassword(newpassword);if(userService.updateById(user)){return Result.ok().put("status","success");}return Result.error("修改失敗");}
// 原密碼驗證失敗return Result.ok().put("status","passwordError");}
密碼修改表單實體類UpdatePasswordFrom
package com.qcby.DTO;import lombok.Data;/*** 密碼修改表單實體類* 用于接收前端傳遞的舊密碼修改相關參數*/
@Data
public class UpdatePasswordFrom {/*** 當前密碼(舊密碼)*/private String password;/*** 新密碼*/private String newPassword;/*** 確認新密碼(可選,用于前端二次驗證)*/private String confirmPassword;
}
?結果
3.退出登錄模塊
實現思路
①登出請求:將當前session設置為無效
②將token設置為空
③將router設置為空
④將cookie里面的token信息清空
⑤返回登錄頁面
登出請求處理
/*** 用戶退出* @param session* @return*/@RequestMapping("/logout")public Result logOut(HttpSession session){session.invalidate();return Result.ok();}
結果圖
4.小區的增刪改查
對應的實體類
package com.qcby.entity;import lombok.Data;import java.util.Date;@Data
public class Community {private Integer communityId;private String communityName;private Integer termCount;private Integer seq;private String creater;private Date createTime;private Float lng;private Float lat;
}
服務層
package com.qcby.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qcby.DTO.CommunityListFrom;
import com.qcby.entity.Community;
import com.qcby.vo.PageVO;public interface ICommunityService extends IService<Community> {PageVO communityList(CommunityListFrom communityListForm);
}
增刪改查的實現思路?
對于傳入參數,如果有,但是參數不多,只有一個,直接接收;如果參數較多,應將其傳入字段封裝在一個實體類中。對于get請求,用@PathVariable接收,對于非get請求,用@RequestBody接收。
對于返回參數,如果與數據單表中的字段可以進行一一映射,不需要考慮封裝,直接返回;如果無法在單表中進行一一映射,則需要根據返回參數進行實體類封裝。
對于單表的增刪改查,mybatiaPlus提供了相應的操作,對于多表則需要手寫sql語句
4.1小區搜索和查詢
傳入參數
{"page": 1,"limit": 10,"communityId": "","communityName": ""
}
返回參數
{"msg": "操作成功","code": 200,"data": {"totalCount": 8,"pageSize": 10,"totalPage": 1,"currPage": 1,"list": [{"communityId": 19,"communityName": "北清云際","termCount": 33,"seq": 190,"creater": "admin","createTime": "2023-07-18 00:41:20","lng": 116.298904,"lat": 40.091644,"personCnt": 1},{"communityId": 17,"communityName": "天龍苑","termCount": 10,"seq": 170,"creater": "admin","createTime": "2023-07-18 00:38:06","lng": 116.36206,"lat": 40.088108,"personCnt": 0}]}
}
?實現思路:由于返回list中在community表中沒有personCnt字段,因此需要封裝一個CommunityVO類用于數據的返回。同時由于mybtisPlus中并沒有現成的sql用于根據community_id查詢相應的居民人數,因此需要編寫sql用來查詢每個community對于的人數personCnt
CommunityVO類
package com.qcby.vo;import lombok.Data;import java.util.Date;@Data
public class CommunityVO {private Integer communityId;private String communityName;private Integer termCount;private Integer seq;private String creater;private Date createTime;private Float lng;private Float lat;private Integer personCnt;
}
?controller
/*** 獲取小區的所有信息,分頁條件查詢* @return*/@GetMapping("/list")public Result getList(CommunityListFrom communityListFrom){PageVO pageVO = this.communityService.communityList(communityListFrom);return Result.ok().put("data",pageVO);}
service?
// CommunityServiceImpl.java
package com.qcby.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcby.DTO.CommunityListFrom;
import com.qcby.entity.Community;
import com.qcby.mapper.CommunityMapper;
import com.qcby.service.ICommunityService;
import com.qcby.vo.CommunityVO;
import com.qcby.vo.PageVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
public class CommunityServiceImpl extends ServiceImpl<CommunityMapper, Community> implements ICommunityService {@Autowiredprivate CommunityMapper communityMapper;@Overridepublic PageVO communityList(CommunityListFrom communityListForm) {Page<Community> page = new Page<>(communityListForm.getPage(), communityListForm.getLimit());QueryWrapper<Community> queryWrapper = new QueryWrapper<>();queryWrapper.like(StringUtils.isNotBlank(communityListForm.getCommunityName()), "community_name", communityListForm.getCommunityName());Page<Community> resultPage = this.communityMapper.selectPage(page, queryWrapper);PageVO pageVO = new PageVO();List<CommunityVO> list = new ArrayList<>();for (Community record : resultPage.getRecords()) {CommunityVO communityVO = new CommunityVO();BeanUtils.copyProperties(record, communityVO);Integer personCount = communityMapper.getCountByCommunityId(record.getCommunityId());communityVO.setPersonCnt(personCount);list.add(communityVO);}pageVO.setList(list);pageVO.setTotalCount(resultPage.getTotal());pageVO.setPageSize(resultPage.getSize());pageVO.setCurrPage(resultPage.getCurrent());pageVO.setTotalPage(resultPage.getPages());return pageVO;}
}
?根據communityId查詢居民人數的mapper
@Select({"select count(*) from person where community_id = #{communityId} "})Integer getCountByCommunityId(Integer communityId);
分頁?
用于分頁封裝的實體類
package com.qcby.vo;import lombok.Data;import java.util.List;@Data
public class PageVO {private Long totalCount;private Long totalPage;private Long pageSize;private Long currPage;private List list;
}
分頁的相關配置?
package com.qcby.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisPlusConfig {/*** 配置分頁插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加分頁攔截器(支持MySQL、PostgreSQL等主流數據庫)interceptor.addInnerInterceptor(new PaginationInnerInterceptor());//添加樂觀鎖插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
結果圖
4.2添加小區
傳入參數
{"communityName": "test""lat": "1""lng": "1""seq": 1"termCount": "1"
}
返回參數
{"msg": "操作成功","code": 200
}
controller
/*** 添加小區* @param community* @param session* @return*/@RequestMapping("/add")public Result add(@RequestBody Community community, HttpSession session){User user = (User) session.getAttribute("user");community.setCreater(user.getUsername());boolean save = this.communityService.save(community);if(!save) return Result.error("添加失敗");return Result.ok();}
界面
4.3通過id查詢小區
傳入參數
1
返回參數
{"msg": "操作成功","code": 200,"data": {"communityId": 19,"communityName": "北清云際","termCount": 33,"seq": 190,"lng": 116.298904,"lat": 40.091644}
}
controller
/*** 修改的回顯操作* @param id* @return*/@RequestMapping("/info/{id}")public Result info(@PathVariable("id") Integer id){Community community = this.communityService.getById(id);if(community == null){return Result.error("沒有此小區");}return Result.ok().put("data",community);}
界面
?4.4修改小區
傳入參數
{"communityId": 21,"communityName": "1","termCount": "12","lng": "12","lat": "12","seq": 210
}
返回參數
{"msg": "操作成功","code": 200
}
controller
/*** 修改小區信息* @param community* @return*/@RequestMapping("/edit")public Result edit(@RequestBody Community community){boolean updateById = this.communityService.updateById(community);if(!updateById) return Result.error("修改失敗");return Result.ok();}
界面
4.5刪除小區
傳入參數
[21,20
]
返回參數
{"msg": "操作成功","code": 200
}
controller
/*** 僅刪除小區信息,不刪除關聯表數據(不使用try-catch)* @param ids 小區ID數組* @return 操作結果*/@RequestMapping("/del")@Transactionalpublic Result del(@RequestBody Integer[] ids) {// 刪除小區數據boolean remove = this.communityService.removeByIds(Arrays.asList(ids));if (!remove) {return Result.error("小區刪除失敗");}return Result.ok();}
界面
注意:在刪除小區的同時也會刪除掉這個小區對應的攝像頭、訪客記錄、小區出入記錄、小區居民,如果數據庫中沒有則會個小區對應的內容則會刪除失敗
5.攝像頭的增刪改查
和上面的小區的實現基本一致
對應的Controller
package com.qcby.controller;import com.qcby.entity.Camera;
import com.qcby.entity.User;
import com.qcby.service.ICameraService;
import com.qcby.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpSession;
import java.util.Arrays;
import java.util.List;@RestController
@RequestMapping("/sys/camera")
public class CameraController {@Autowiredprivate ICameraService cameraService;/*** 查詢攝像頭列表*/@GetMapping("/list/{communityId}")public Result getCameraList(@PathVariable Integer communityId) {try {// 調用關聯查詢方法,獲取包含小區名稱的攝像頭列表List<Camera> cameraList = cameraService.getCameraListWithCommunityName(communityId);return Result.ok().put("data", cameraList);} catch (Exception e) {return Result.error("查詢失敗:" + e.getMessage());}}/*** 添加攝像頭*/@RequestMapping("/add")public Result add(@RequestBody Camera camera, HttpSession session){User user = (User) session.getAttribute("user");camera.setCreater(user.getUsername());boolean save = cameraService.save(camera);if(!save) return Result.error("添加失敗");return Result.ok();}/*** 修改的回顯操作* @param id* @return*/@RequestMapping("/info/{id}")public Result info(@PathVariable("id") Integer id){Camera camera = cameraService.getById(id);if(camera == null){return Result.error("沒有此攝像頭");}return Result.ok().put("data",camera);}/*** 修改攝像頭信息* @param camera* @return*/@RequestMapping("/edit")public Result edit(@RequestBody Camera camera){boolean updateById = cameraService.updateById(camera);if(!updateById) return Result.error("修改失敗");return Result.ok();}/*** 刪除攝像頭信息* @param ids 攝像頭ID數組* @return 操作結果*/@RequestMapping("/del")public Result del(@RequestBody Integer[] ids){boolean remove = cameraService.removeByIds(Arrays.asList(ids));if(!remove) return Result.error("刪除失敗");return Result.ok();}
}
對應的實體類
package com.qcby.entity;import lombok.Data;@Data
public class Person {private Integer personId;private Integer communityId;private String termName;private String houseNo;private String userName;private String sex;private String mobile;private String faceUrl;private String personType;private Integer state;private String creater;private String createTime;private String remark;private String faceBase;
}
對應的Mapper.java
package com.qcby.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.entity.Camera;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface CameraMapper extends BaseMapper<Camera> {// 聯表查詢,通過 camera 的 community_id 關聯 community 表,獲取小區名稱@Select("SELECT c.*, com.community_name as communityName " +"FROM camera c " +"LEFT JOIN community com ON c.community_id = com.community_id " +"WHERE c.community_id = #{communityId}")List<Camera> selectCameraWithCommunityName(@Param("communityId") Integer communityId);
}
應的Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.mapper.CameraMapper"><!-- 與數據庫表字段嚴格映射 --><resultMap id="BaseResultMap" type="com.qcby.entity.Camera"><id column="camera_id" property="cameraId" /><result column="community_id" property="communityId" /><result column="camera_name" property="cameraName" /><result column="mac_id" property="macId" /><result column="direction" property="direction" /><result column="state" property="state" /><result column="seq" property="seq" /><result column="creater" property="creater" /><result column="create_time" property="createTime" /><result column="remark" property="remark" /></resultMap><!-- 完整查詢字段列表 --><sql id="Base_Column_List">camera_id,community_id,camera_name,mac_id,direction,state,seq,creater,create_time,remark</sql><!-- selectById 實現(MyBatis-Plus 基礎方法) --><select id="selectById" resultMap="BaseResultMap">SELECT<include refid="Base_Column_List" />FROM cameraWHERE camera_id = #{id}</select><!--修改攝像頭信息--><update id="updateById" parameterType="com.qcby.entity.Camera">UPDATE camera<set><if test="et.cameraName != null">camera_name = #{et.cameraName},</if><if test="et.macId != null"> mac_id = #{et.macId},</if><if test="et.direction != null"> direction = #{et.direction},</if><if test="et.state != null"> state = #{et.state},</if><if test="et.seq != null"> seq = #{et.seq},</if><if test="et.remark != null"> remark = #{et.remark},</if></set>WHERE camera_id = #{et.cameraId}</update><!--刪除攝像頭--><delete id="deleteBatchIds" parameterType="java.util.Collection">DELETE FROM cameraWHERE camera_id IN<foreach item="id" collection="coll" separator="," open="(" close=")">#{id}</foreach></delete>
</mapper>
對應的ICameraService
package com.qcby.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qcby.entity.Camera;import java.util.List;public interface ICameraService extends IService<Camera> {List<Camera> getCameraListWithCommunityName(Integer communityId);
}
對應的CameraServiceImpl
package com.qcby.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcby.entity.Camera;
import com.qcby.mapper.CameraMapper;
import com.qcby.service.ICameraService;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class CameraServiceImpl extends ServiceImpl<CameraMapper, Camera> implements ICameraService {@Overridepublic List<Camera> getCameraListWithCommunityName(Integer communityId) {return baseMapper.selectCameraWithCommunityName(communityId);}
}