前情提要
上一篇弄好了菜单和角色, 剩余用户和角色关联还没做, 动态菜单还没做, html
按钮控制也没做, 这篇都弄完, 然后knife4j也整合进去, 上一篇太长了,
所以都留在这一篇完成了
直接进入主题,开整
SysUserEntity
增加非表字段roles
//多个角色id,非表字段
@TableField(exist = false)
private List<Integer> roles;
改造sysUserController
改动处加了注释,增删改查
四个接口都动过
import com.macro.Vo.UserParam;
import com.macro.entity.UserEntity;
import com.macro.service.UserService;
import com.macro.utils.Result;
import com.macro.utils.shiro.ShiroUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("sysUser")
public class sysUserController {
@Autowired
private UserService userService;
@GetMapping("list")
public Result list(UserParam param){
Result result = userService.findByParam(param);
return result;
}
@PostMapping("add")
public Result add(@RequestBody UserEntity user){
//密码加密
user.setPassword(ShiroUtils.getMdPassWord(user.getPassword()));
//改动
boolean save = userService.insert(user);
if(save){
return Result.success();
}
return Result.error("添加失败");
}
//只添加了@RequiresPermissions("sys:user:info")
@RequiresPermissions("sys:user:info")
@GetMapping("info/{id}")
public Result info(@PathVariable("id") Integer id){
//改动
UserEntity userEntity = userService.findById(id);
return Result.success(userEntity);
}
@PostMapping("update")
public Result update(@RequestBody UserEntity user){
user.setPassword(ShiroUtils.getMdPassWord(user.getPassword()));
//改动
boolean type = userService.updateData(user);
return type ? Result.success() : Result.error("更新失败");
}
@PostMapping("del")
public Result del(@RequestBody String[] ids){
if(ids.length > 0){
//改动
boolean type = userService.delIds(ids);
return type ? Result.success() : Result.error("删除失败");
}
return Result.success();
}
@GetMapping("setting")
public Result setting(){
//直接通过ShiroUtils.getUserId()获取用户id
UserEntity userEntity = userService.getById(ShiroUtils.getUserId());
return Result.success(userEntity);
}
}
UserService
import com.baomidou.mybatisplus.extension.service.IService;
import com.macro.Vo.UserParam;
import com.macro.entity.UserEntity;
import com.macro.utils.Result;
public interface UserService extends IService<UserEntity> {
UserEntity findByUserName(String username);
Result findByParam(UserParam param);
boolean insert(UserEntity user);
UserEntity findById(Integer id);
boolean updateData(UserEntity user);
boolean delIds(String[] ids);
}
UserServiceImpl
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.macro.Vo.UserParam;
import com.macro.dao.UserDao;
import com.macro.entity.SysUserRoleEntity;
import com.macro.entity.UserEntity;
import com.macro.service.SysUserRoleService;
import com.macro.service.UserService;
import com.macro.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private SysUserRoleService userRoleService;
@Override
public UserEntity findByUserName(String username) {
QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
return userDao.selectOne(wrapper);
}
//新增
@Override
public Result findByParam(UserParam param) {
PageHelper.startPage(param.getPage(), param.getLimit());
List<UserEntity> list = userDao.findByParam(param);
PageInfo<UserEntity> pageInfo = new PageInfo<>(list);
return Result.success(0,pageInfo.getTotal(),list);
}
//新增
@Override
public boolean insert(UserEntity user) {
int insert = userDao.insert(user);
if(insert > 0){
List<Integer> roles = user.getRoles();
if(roles != null && roles.size() > 0){
for (int i = 0; i < roles.size(); i++) {
SysUserRoleEntity entity = new SysUserRoleEntity();
entity.setRoleId(roles.get(i));
entity.setUserId(user.getId());
userRoleService.save(entity);
}
}
}
return insert>0?true:false;
}
//新增
@Override
public UserEntity findById(Integer id) {
UserEntity user = userDao.selectById(id);
if(user!= null){
List<SysUserRoleEntity> roleList = userRoleService.list(new QueryWrapper<SysUserRoleEntity>().eq("user_id",id));
user.setRoles(roleList.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList()));
}
return user;
}
//新增
@Override
public boolean updateData(UserEntity user) {
List<Integer> roles = user.getRoles();
List<SysUserRoleEntity> roleList = userRoleService.list(new QueryWrapper<SysUserRoleEntity>().eq("user_id",user.getId()));
List<Integer> list = roleList.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList());
//新的id如果没在旧的中查询出来,则是新增的
for (int i = 0; i < roles.size(); i++) {
if(!list.contains(roles.get(i))){
SysUserRoleEntity entity = new SysUserRoleEntity();
entity.setUserId(user.getId());
entity.setRoleId(roles.get(i));
userRoleService.save(entity);
}
}
//旧的没有在新的里面查出来,则是要删除的
for (int i = 0; i < roleList.size(); i++) {
if(!roles.contains(roleList.get(i).getRoleId())){
userRoleService.removeById(roleList.get(i).getId());
}
}
int i = userDao.updateById(user);
return i >0?true:false;
}
//新增
@Override
public boolean delIds(String[] ids) {
for (int i = 0; i < ids.length; i++) {
List<SysUserRoleEntity> list = userRoleService.list(new QueryWrapper<SysUserRoleEntity>().eq("user_id", ids[i]));
List<Integer> userRoleIds = list.stream().map(SysUserRoleEntity::getId).collect(Collectors.toList());
userRoleService.removeByIds(userRoleIds);
userDao.deleteById(ids[i]);
}
return true;
}
}
sysUser.html
改动
性别
下方增加角色多选下拉框
<div class="layui-form-item">
<label class="layui-form-label">角色 </label>
<div class="layui-input-inline">
<el-select v-model="user.roles" multiple placeholder="可多选">
<el-option v-for="item in roleList"
:value="item.id" :key="item.id" :label="item.roleName"></el-option>
</el-select>
</div>
</div>
sysUser.js
data
中增加roleList :[]
mounted
中调用 this.initRoles();
//新增初始化请求
initRoles(){
axios({
url:"role/roleList",
method: "get"
}).then(res =>{
if(res.data.code == 200){
vm.roleList = res.data.data;
}
});
},
SysRoleController
新增roleList
//初始化获取所有角色
@GetMapping("roleList")
public Result roleList(){
List<SysRoleEntity> list = roleService.list();
return Result.success(list);
}
运行之后是没什么问题的
动态目录
删除掉index.html
中的这一块,并且给ul
增加 id="menu"
, 目录和菜单都在js中完成
js
部分增加menuList
,并将menuList
方法在mounted
中调用
menuList
中有两个for
第一层的for
为目录,第二层为菜单,菜单可以点击,所以增加jump
方法,像上面那种图设置跳转地址会跳出iframe
所以自定义了一个jump
方法,也算一个坑,最下方加入了一个layui
的重新渲染
menuList
代码
menuList() {
//手动赋值,vue与layui不兼容
axios({
url: "menu/user/list",
methods: "get"
}).then(res => {
let list = res.data.data;
if (list != null && list.length > 0) {
let li = "";
for (let i = 0; i < list.length; i++) {
let menu = "<li class='layui-nav-item' style='background-color: #001529 !important;'>" +
"<a href='javascript:;' > <i class='layui-icon "+list[i].icon+"'> "+list[i].name+"</i></a><dl class='layui-nav-child'>";
if(list[i].childList != null && list[i].childList.length){
for (let j = 0; j <list[i].childList.length; j++) {
menu = menu + "<dd><a onclick=jump('"+list[i].childList[j].path + "','"+list[i].childList[j].name+"')"+" >"+
"<i class='layui-icon "+list[i].childList[j].icon+"'> "+list[i].childList[j].name+"</i> </a></dd>"
}
}
menu = menu + "</dl></li>";
li = li + menu;
}
$("#menu").html(li);
}
//重新渲染
layui.use('element', function() {
var element = layui.element;
element.init();
});
});
},
jump
//页面跳转,重新修改跳转方式
function jump(href,name){
vm.val =name;
vm.main = href;
}
SysMenuController
编写menu/user/list
接口
//目录菜单
@GetMapping("user/list")
public Result userList(){
List<SysMenuEntity> list = menuService.findListByUserId(ShiroUtils.getUserId());
return Result.success(list);
}
SysMenuService
List<SysMenuEntity> findListByUserId(Integer userId);
SysMenuServiceImpl
@Override
public List<SysMenuEntity> findListByUserId(Integer userId) {
List<SysMenuEntity> menuList = this.findByUserId(userId);
List<SysMenuEntity> hierarchyList = getHierarchyList(menuList);
return hierarchyList;
}
修改一下sys_menu
中path
数据和icon
数据
运行效果,修改了一下角色关联的目录,也是ok的
整体结束
看似结束了,基本算是做完了,还有一个按钮控制没做,这块推翻了我很多想法,比如yml
配置中设置了非强制的跳转,
但是也造成了一个坑,也就是html
中shiro
控制按钮,如果不从controlle
r跳转过去,则无法生效,因为引入的一个jar包名称就叫做thymeleaf-extras-shiro
,所以yml
那个配置得注释掉,还得设置各种跳转的代码
将IndexController
改为PathController
PathController
全部代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PathController {
@GetMapping(value = {"index","","/"})
public String index(){
return "index";
}
@GetMapping(value = {"user"})
public String user(){
return "sysUser";
}
@GetMapping(value = {"info"})
public String info(){
return "info";
}
@GetMapping(value = {"main"})
public String main(){
return "main";
}
@GetMapping(value = {"role"})
public String role(){
return "sysRole";
}
@GetMapping(value = {"menu"})
public String menu(){
return "sysMenu";
}
}
数据库sys_menu
修改path
index.html
main
直接对应controller
的main
接口
运行看一下效果,没什么问题
thymeleaf
整合shiro
标签
pom.xml
增加依赖
<!-- properties下的 -->
<thymeleaf-shiro>2.0.0</thymeleaf-shiro>
<!-- thymeleaf整合shiro标签 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>${thymeleaf-shiro}</version>
</dependency>
ShiroConfig
中增加shiroDialect
加入这个方法便能在html中使用shiro的按钮控制了,当前角色控制也是可以,只是使用的东西不一样
/**
* 添加ShiroDialect 为了在thymeleaf里使用shiro的标签的bean
* @return
*/
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
sysUser.html
为增删改查
按钮添加控制
shiro:hasPermission="sys:user:save"
控制权限的(Permission
) 如果是Role
就是角色了,别写错了
<button shiro:hasPermission="sys:user:save" type="button" class="layui-btn" @click="add">新增</button>
<button shiro:hasPermission="sys:user:update" type="button" class="layui-btn layui-btn-normal" @click="update">修改</button>
<button shiro:hasPermission="sys:user:del" type="button" class="layui-btn layui-btn-danger" @click="del">删除</button>
LoginController
修改退出
退出之后清除掉登陆人的一些缓存信息,进入时重新加载数据,保存缓存的一致
@GetMapping("logout")
public String logout(){
//清理缓存,并且跳转到登陆页面
SecurityUtils.getSubject().logout();
return "login";
}
测试一下,修改了角色权限,需要重新登陆才会生效,全部完成了
项目完结
项目基本结束了,权限校验只是拿出来部分做演示,没有全部去写,无论是controller
的注解
还是html
的标签
,都只是写了一部分,还有一些特别的点没做,比如事务,权限更新之后刷新redis
内部的权限缓存,这两点都没去做,当然还有layui
下次就直接用Elemenet UI
不会用Layui
毕竟和vue
用还是有好多不舒服的地方,比如还需要手动设置刷新,下拉控件
、redio
使用vue
无效、毕竟两个东西走的方向不太一样,所以下次直接使用element ui
或者iview ui
项目结束了,剩下就是整合knife4j
生成好看的接口文档了,还有上传linux
直接线上访问
整合knife4j
pim.xml
添加knife4j的jar
<!-- properties -->
<knife4j-version>2.0.2</knife4j-version>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j-version}</version>
</dependency>
添加配置
config
文件夹下添加Swagger2Config
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Swagger2Config {
@Bean(value = "indexApi")
public Docket indexApi() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.macro.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("后台项目管理")
.description("无术同学")
.version("1.0")
.build();
}
}
原本想在getShiroFilterFactoryBean
中放行swagger
直接在外部访问的,想想算了,不安全
sysUserController
修改
添加如下几个注解
修改UserParam
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
//继承PageEntity
@Data
//swagger的注解
@ApiModel(value = "用户参数")
public class UserParam extends PageEntity {
//昵称,添加这个注解能在文档上看到参数名称
@ApiModelProperty(value = "昵称")
private String niceName;
//账号
@ApiModelProperty(value = "账号")
private String username;
}
修改PageEntity
@Data
public class PageEntity {
// 页数
@ApiModelProperty(value = "页数")
private Integer page;
//每页数量
@ApiModelProperty(value = "每页数量")
private Integer limit;
}
菜单中添加接口文档菜单
然后就能看到如下图片了,只改了一小部分,只能登陆之后访问,或者修改
getShiroFilterFactoryBean
放行doc.html
等资源,能直接看到,登陆之后可以直接访问http://127.0.0.1:8082/doc.html#/home
也可以看到!
源码
在公众号内发送后台
即可获取源码
和数据库