前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实现基于用户角色的页面路由资源权限控制(后端篇)

实现基于用户角色的页面路由资源权限控制(后端篇)

作者头像
用户3587585
发布2021-09-08 15:13:59
2.2K0
发布2021-09-08 15:13:59
举报
文章被收录于专栏:阿福谈Web编程阿福谈Web编程

0 引言

最近在公司里做了一个基于用户角色的页面路由资源权限控制的需求,前后端分离结合起来难度还是挺大的,去年也做过一个类似的需求,把前后端打通花了好天时间。当时就想写一篇关于权限控制的实战文章,但是无奈数据属于公司的保密级别,不好造数据就搁浅了。

如果仅仅是限制后台接口的权限或者前端路由列表是静态的,每次添加新的页面就往路由文件里加路由组件,那样实现起来倒是没什么挑战。后台用spring-security或者shiro可以搞定,前端使用vue-router可轻松搞定。现在的需求是要求用户登录后根据其角色加载具有权限的页面和可访问的路由列表,就是要求动态加载系统左侧的菜单。后面的权限控制页面要求能给用户分配角色、给角色动态添加页面权限等都涉及到了前后端结合控制用户的对资源和按钮的访问权限。

对于精通java的开发人员来说,这样的需求后台的难度不是很大,前端结合vuexvue-router从后台取数据实现动态页面路由跳转权限控制才是一大难点。限于文章篇幅,本文先解决后台有关权限控制的建表和接口需求,下一篇文章再结合前端vuexvue-router组件实现完整的需求,并在页面上看到效果。

这篇文章以笔者上一篇文章介绍一个开源博客项目VBlog并打包部署到已存在运行项目的Nginx服务器下 为基础,大部分表已建好,只需要再添加两张与实现权限控制需求相关的表。

1 与权限相关的表

1.1 user表
代码语言:javascript
复制
-- 建表脚本
DROP  TABLE  IF EXISTS  `user`;
CREATE  TABLE  `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(64) DEFAULT NULL,
  `nickname` varchar(64) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `enabled` tinyint(1) DEFAULT '1',
  `email` varchar(64) DEFAULT NULL,
  `userface` varchar(255) DEFAULT NULL,
  `regTime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21  DEFAULT  CHARSET=utf8;

-- 添加数据
-- Records of user
-- ----------------------------
INSERT  INTO  `user`  VALUES ('6', 'linghu', '令狐葱', '202cb962ac59075b964b07152d234b70', '1', 'linghu@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920326&di=44a6fa6b597d86f475c2b15fa93008dd&imgtype=0&src=http%3A%2F%2Fwww.qqzhi.com%2Fuploadpic%2F2015-01-12%2F023019564.jpg', '2017-12-08 09:30:22');
INSERT  INTO  `user`  VALUES ('7', 'sang', '江南一点雨', '202cb962ac59075b964b07152d234b70', '1', ' sang123@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-21 13:30:29');
INSERT  INTO  `user`  VALUES('10', 'qiaofeng', '乔峰', '202cb962ac59075b964b07152d234b70', '1', 'qiaofeng@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('13', 'duanzhengchun', '段正淳', '202cb962ac59075b964b07152d234b70', '0', 'duanzhengchun@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('14', 'chenjialuo', '陈家洛', '202cb962ac59075b964b07152d234b70', '0', 'chenjialuo@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('15', 'yuanchengzhi', '袁承志', '202cb962ac59075b964b07152d234b70', '1', 'yuanchengzhi@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('16', 'chuliuxiang', '楚留香', '202cb962ac59075b964b07152d234b70', '1', 'chuliuxiang@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('17', 'baizhantang', '白展堂', '202cb962ac59075b964b07152d234b70', '0', 'baizhantang@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('18', 'renwoxing', '任我行', '202cb962ac59075b964b07152d234b70', '1', 'renwoxing@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES ('19', 'zuolengchan', '左冷禅', '202cb962ac59075b964b07152d234b70', '1', 'zuolengchan@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');
INSERT  INTO  `user`  VALUES('20', 'fengqingyang', '风清扬', '202cb962ac59075b964b07152d234b70', '1', 'fengqingyang@qq.com', 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg', '2017-12-24 06:30:46');

该表项目中原有,无需新建

1.2 roles表
代码语言:javascript
复制
-- 建表脚本,为了方便路由资源表关联角色表,该表需要修改
DROP  TABLE  IF  EXISTS  `roles`;
CREATE  TABLE  `roles` (
  `id`  int(11) NOT NULL AUTO_INCREMENT comment  '角色ID',
   role_code varchar(30) null  comment  '角色编码'  
   role_name varchar(32) null  comment  '角色名称',
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6  DEFAULT  CHARSET=utf8;

-- Records of roles
-- ----------------------------
INSERT  INTO  roles(role_code,role_name) VALUES ('superAdmin', '超级管理员');
INSERT  INTO  roles(role_code,role_name) VALUES ('generalUser', '普通用户');
INSERT  INTO  roles(role_code,role_name) VALUES ('test1', '测试角色1');
INSERT  INTO  roles(role_code,role_name) VALUES ('test2', '测试角色2');
INSERT  INTO  roles(role_code,role_name) VALUES ('test3', '测试角色3');

该表可先删除,再新建

1.3 roles_user表
代码语言:javascript
复制
DROP  TABLE  IF EXISTS  `roles_user`;
CREATE  TABLE  `roles_user` (
  `id`  int(11) NOT  NULL AUTO_INCREMENT,
  `rid`  int(11) DEFAULT  '2',
  `uid`  int(11) DEFAULT  NULL,
  PRIMARY KEY (`id`),
  KEY `rid` (`rid`),
  KEY  `roles_user_ibfk_2`(`uid`),
  CONSTRAINT  `roles_user_ibfk_1`  FOREIGN KEY(`rid`) REFERENCES  `roles`(`id`),
  CONSTRAINT  `roles_user_ibfk_2`  FOREIGN KEY(`uid`) REFERENCES  `user`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=131  DEFAULT  CHARSET=utf8;

-- Records of roles_user
-- ----------------------------
INSERT  INTO  `roles_user`  VALUES('8', '2', '7');
INSERT  INTO  `roles_user`  VALUES('9', '1', '7');
INSERT  INTO  `roles_user`  VALUES('17', '5', '7');
INSERT  INTO  `roles_user`  VALUES('106', '2', '14');
INSERT  INTO  `roles_user`  VALUES('108', '2', '16');
INSERT  INTO  `roles_user`  VALUES('109', '2', '17');
INSERT  INTO  `roles_user`  VALUES('110', '2', '18');
INSERT  INTO  `roles_user`  VALUES('111', '2', '19');
INSERT  INTO  `roles_user`  VALUES('112', '2', '20');
INSERT  INTO  `roles_user`  VALUES('119', '2', '15');
INSERT  INTO  `roles_user`  VALUES('120', '5', '15');
INSERT  INTO  `roles_user`  VALUES('121', '2', '6');
INSERT  INTO  `roles_user`  VALUES('123', '2', '13');
INSERT  INTO  `roles_user`  VALUES('124', '3', '13');
INSERT  INTO  `roles_user`  VALUES('128', '2', '10');
INSERT  INTO  `roles_user`  VALUES('129', '5', '10');
INSERT  INTO  `roles_user`  VALUES('130', '1', '6');

该表项目中原有,无需新建

1.4 router_resources(路由资源)表
代码语言:javascript
复制
drop  table  if  exists router_resources;
create  table router_resources(
  id  int auto_increment comment  '主键',
  pid int  comment  '父路由ID',
  name  varchar(50) not  null  comment '路由名称',
  title varchar(50) comment  '路由展示名称',
  icon varchar(20) comment  '图标',
  hidden smallint  comment  '是否隐藏:0-展示;1-隐藏',
  level  smallint  comment  '层级',
  path  varchar(100) comment  '路径',
  componnet_url varchar(200) not  null  comment  '组件完整url',
  order_no int  comment '顺序号',
  keep_alive smallint  not  null  default  0,
  created_by varchar(64) not  null  comment  '创建人',
  created_time TIMESTAMP   not  null  comment  '创建时间',
  last_updated_by varchar(64) comment  '最后更新人',
  last_updated_time TIMESTAMP   comment  '更新时间',
  primary key (id)
)ENGINE=InnoDB  DEFAULT  CHARSET=utf8 comment '路由资源表';

insert  into router_resources(pid,name,title,icon,hidden,level,path,componnet_url,order_no,created_by,created_time)
values(0,'articleManagement','文章管理','fa fa-file-text-o',0,1,'/home','@/components/Home',1,'sang',now());

insert  into router_resources(pid,name,title,icon,hidden,level,path,componnet_url,order_no,created_by,created_time)
values(0,'userManagement','用户管理','fa fa-user-o',0,1,'/home','@/components/Home',2,'sang',now());

insert  into router_resources(pid,name,title,icon,hidden,level,path,componnet_url,order_no,created_by,created_time)
values(0,'categoriesManagement','栏目管理','fa fa-reorder',0,1,'/home','@/components/Home',3,'sang',now());

insert  into router_resources(pid,name,title,icon,hidden,level,path,componnet_url,order_no,created_by,created_time)
values(0,'dataStatics','数据统计','fa fa-bar-chart',0,1,'/home','@/components/Home',4,'sang',now());

insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(1,'articleList','文章列表','',0,2,'/articleList','@/components/ArticleList',11,
       'sang',now(),'sang',now(),1);

insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(1,'publishArticle','发表文章','',0,2,'/postArticle','@/components/PostArticle',12,
       'sang',now(),'sang',now(),0);


insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(1,'blogDetail','博客详情','',1,2,'/blogDetail','@/components/BlogDetail',13,
       'sang',now(),'sang',now(),0);

insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(1,'editBlog','编辑博客','',1,2,'/editBlog','@/components/PostArticle',14,
       'sang',now(),'sang',now(),0);


insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(2,'userManagement','用户管理','fa fa-user-o',0,2,'/user','@/components/UserMana',21,
       'sang',now(),'sang',now(),0);

insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(3,'cateManagement','栏目管理','fa fa-reorder',0,2,'/cateMana','@/components/CateMana',31,
       'sang',now(),'sang',now(),0);

insert  into router_resources(pid,name,title,icon,hidden,level,
                         path,componnet_url,order_no,created_by,created_time,
                         last_updated_by,last_updated_time,keep_alive)
values(4,'dataStatics','数据统计','fa fa-bar-chart',0,2,'/charts','@/components/DataCharts',41,
       'sang',now(),'sang',now(),0);

路由资源表是根据前端vueblog项目src/router/index.js 中路由项来定义字段

1.5 role_resources(角色资源)表
代码语言:javascript
复制
create  table role_resources(
  id  int(11) not  null auto_increment comment  '主键',
  role_id int(11)  not  null  comment  '角色ID',
  resource_id int(11) not  null  comment  '资源ID',
  created_by varchar(64) not  null  comment  '创建人',
  created_time TIMESTAMP   not  null  comment  '创建时间',
  last_updated_by varchar(64) comment  '最后更新人',
  last_updated_time TIMESTAMP   comment  '更新时间',
  primary key (id)
)ENGINE=InnoDB  DEFAULT  CHARSET=utf8;

-- 使用root用户完成添加联合唯一索引
create  unique  index uk_role_resource on role_resources(role_id,resource_id);

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,1,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,2,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,3,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,4,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,5,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,6,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,7,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,8,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,9,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,10,'sang',now(),'sang',now());

insert  into role_resources(role_id, resource_id, created_by, created_time, last_updated_by, last_updated_time)
values(7,11,'sang',now(),'sang',now());

新建表

五个表之间的关系图

为实现权限控制,在数据库中建了5张用于控制权限的表,分别是User表、Roles表、roles_user表、router_resources表和role_resources表,其中roles_user表中用户与角色是多对多的关系,role_resources表中角色与菜单资源也是多对多的关系。

2 根据角色ID查询资源列表接口

2.1 RouterResourceRouterResourceVo
代码语言:javascript
复制
public  class RouterResource implements Serializable {

   private Integer id;

   private Integer pid;

   private String name;

   private String title;

   private String icon;

   private Integer hidden;

   private Integer level;

   private String path;

   private String componentUrl;

   private Integer orderNo;

   private String createdBy;

   private Date createdTime;

   private String lastUpdatedBy;

   private Date lastUpdatedTime;

   private Integer keepAlive;
    // ......省略setter和gettter方法
}
代码语言:javascript
复制
public  class RouterResourceVo implements Serializable {
    // 主键ID
    private Integer id;
    // 路由资源名称
    private String name;
    // 路由资源标题
    private String title;
    // 路由资源图标(也可以称菜单图标)
    private String icon;
    // 是否隐藏:0-false;1-true
    private boolean hidden;
    // 菜单所在层级
    private Integer level;
    // 菜单路径
    private String path;
    // 菜单组件全路径
    private String componentUrl;
    // 是否keepAlive:0-false;1-true
    private boolean keepAlive;
    // 子节点集合
    private List<RouterResourceVo> children = new ArrayList<>();
     // ......省略setter和gettter方法
}
2.2 Mapper层代码

RoleRouterMapper.java

代码语言:javascript
复制
@Repository
public  interface RoleRouterMapper {
    // 根据角色ID查询角色下的路由资源列表
    List<RouterResource> queryRoutersByRoleId(Integer roleId);
    
}

RoleRouterMapper.xml

代码语言:javascript
复制
 <select id="queryRoutersByRoleId" parameterType="Integer" resultType="org.sang.bean.RouterResource">
        SELECT id,pid,name,title,icon,hidden,level,path,
               componnet_url as componentUrl,order_no as orderNo,
               keep_alive as keepAlive
        FROM router_resources
        WHERE id in (
          select resource_id from role_resources
          where role_id=#{roleId,jdbcType=INTEGER}
        )
        ORDER BY level
    </select>
2.3 Service层代码
代码语言:javascript
复制
@Service
public  class RoleRouterService {

    @Autowired
    private RoleRouterMapper roleRouterMapper;

    public List<RouterResourceVo> queryRoleRoutersByRoleId(Integer roleId){
        List<RouterResourceVo> routerResourceVos = new ArrayList<>();
        List<RouterResource> routerResources = roleRouterMapper.queryRoutersByRoleId(roleId);
        if(routerResources.size()==0){
            return routerResourceVos;
        }
        Integer maxLevel = routerResources.get(routerResources.size()-1).getLevel();
        // 将不同层级的路由数组对象放到一个map里面
        Map<String,List<RouterResource>> levelRoutersMap = new HashMap<>();
        for(int i=1;i<maxLevel+1;i++){
            levelRoutersMap.put("level"+i,new ArrayList<>());
        }
        for(RouterResource routerResource : routerResources){
            int levelNum = routerResource.getLevel();
            levelRoutersMap.get("level"+levelNum).add(routerResource);
        }
        // 在每一层的下一层节点列表中寻找子节点,避免深度递归
        for(int level=1;level<maxLevel;level++){
             List<RouterResource> levelRouters = levelRoutersMap.get("level"+level);
             List<RouterResource> levelChildrenRouters = levelRoutersMap.get("level"+(level+1));
             if(level==1){
                 for(RouterResource routerResource : levelRouters){
                     RouterResourceVo routerResourceVo = buildRoleRouteVoByRoleRouter(routerResource);
                     for(RouterResource childRouter: levelChildrenRouters){
                         if(childRouter.getPid()== routerResource.getId()){
                             RouterResourceVo childRouterVo = buildRoleRouteVoByRoleRouter(childRouter);
                             routerResourceVo.getChildren().add(childRouterVo);
                         }
                     }
                     routerResourceVos.add(routerResourceVo);
                 }
             }else{
                 for(RouterResource routerResource : levelRouters){
                     RouterResourceVo routerResourceVo = buildRoleRouteVoByRoleRouter(routerResource);
                     for(RouterResource childRouter: levelChildrenRouters){
                         if(childRouter.getPid()== routerResource.getId()){
                             RouterResourceVo childRouterVo = buildRoleRouteVoByRoleRouter(childRouter);
                             routerResourceVo.getChildren().add(childRouterVo);
                         }
                     }
                 }
             }
        }
        return routerResourceVos;
    }

    private RouterResourceVo buildRoleRouteVoByRoleRouter(RouterResource routerResource){
        RouterResourceVo routerResourceVo = new RouterResourceVo();
        routerResourceVo.setId(routerResource.getId());
        routerResourceVo.setName(routerResource.getName());
        routerResourceVo.setTitle(routerResource.getTitle());
        routerResourceVo.setIcon(routerResource.getIcon());
        routerResourceVo.setHidden(routerResource.getHidden()==1);
        routerResourceVo.setPath(routerResource.getPath());
        routerResourceVo.setComponentUrl(routerResource.getComponentUrl());
        routerResourceVo.setLevel(routerResource.getLevel());
        routerResourceVo.setKeepAlive(routerResource.getKeepAlive()==1);
        return routerResourceVo;
    }

}
2.4 Controller层代码
代码语言:javascript
复制
@RestController
@RequestMapping("/routerResource")
publi c class RouterResourceController {

    private static final Logger logger = LoggerFactory.getLogger(RouterResourceController.class);

    @Autowired
    private RoleRouterService roleRouterService;

    /**
     * 获取当前角色的路由资源列表
     * @param roleId
     * @return
     */
    @GetMapping("/currentRoleResources")
    public RespBean queryCurrentRoleRouters(@RequestParam("roleId") Integer roleId){
        logger.info("roleId={}",roleId);
        RespBean respBean = new RespBean("success","查询成功");
        List<RouterResourceVo> routerResourceVos = roleRouterService.queryRoleRoutersByRoleId(roleId);
        respBean.setData(routerResourceVos);
        return respBean;
    }
    
}
2.5 修改认证成功回调方法

WebSecurityConfig.java

代码语言:javascript
复制
@Configuration
publi c class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserService userService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/category/all","/reg").authenticated()
                .antMatchers("/admin/**").hasRole("superAdmin")///admin/**的URL都需要有超级管理员角色,如果使用.hasAuthority()方法来配置,需要在参数中加上ROLE_,如下.hasAuthority("ROLE_超级管理员")
                .anyRequest().authenticated()//其他的路径都是登录后即可访问
                .and().formLogin().loginPage("/login_page").successHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                httpServletResponse.setContentType("application/json;charset=utf-8");
                PrintWriter out = httpServletResponse.getWriter();
                // 要修改的代码:把用户信息写入响应体
                User user = (User) authentication.getPrincipal();
                ObjectMapper objectMapper = new ObjectMapper();
                String userInfo = objectMapper.writeValueAsString(user);
                out.write("{\"status\":\"success\",\"msg\":\"登录成功\",\"userInfo\":"+userInfo+"}");
                out.flush();
                out.close();
            }
        })
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
                        httpServletResponse.setContentType("application/json;charset=utf-8");
                        PrintWriter out = httpServletResponse.getWriter();
                        out.write("{\"status\":\"error\",\"msg\":\"登录失败\"}");
                        out.flush();
                        out.close();
                    }
                }).loginProcessingUrl("/login") //登录URL
                .usernameParameter("username").passwordParameter("password").permitAll()
                .and().logout().permitAll().and().csrf().disable().exceptionHandling().accessDeniedHandler(getAccessDeniedHandler());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/blogimg/**","/index.html","/static/**");
    }

    @Bean
    AccessDeniedHandler getAccessDeniedHandler() {
        return new AuthenticationAccessDeniedHandler();
    }
}

3 接口测试

启动blogserver服务成功之后即可测试接口

3.1 登录接口
代码语言:javascript
复制
POST http://localhost:8081/login
// form-data
{
    "username": "sang",
    "password": "123"
}
// 响应信息
{
    "status": "success",
    "msg": "登录成功",
    "userInfo": {
        "id": 7,
        "username": "sang",
        "password": "202cb962ac59075b964b07152d234b70",
        "nickname": "江南一点雨",
        "enabled": true,
        "roles": [
            {
                "id": 2,
                "roleCode": "generalUser",
                "roleName": "普通用户"
            },
            {
                "id": 1,
                "roleCode": "superAdmin",
                "roleName": "超级管理员"
            },
            {
                "id": 5,
                "roleCode": "test3",
                "roleName": "测试角色3"
            }
        ],
        "email": "sang123@qq.com",
        "userface": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514093920321&di=913e88c23f382933ef430024afd9128a&imgtype=0&src=http%3A%2F%2Fp.3761.com%2Fpic%2F9771429316733.jpg",
        "regTime": 1513834229000
    }
}
3.1 根据角色ID查询路由资源接口
代码语言:javascript
复制
GET http://localhost:8081/routerResource/currentRoleResources?roleId=7
// 响应信息
{
    "status": "success",
    "msg": "查询成功",
    "data": [
        {
            "id": 1,
            "name": "articleManagement",
            "title": "文章管理",
            "icon": "fa fa-file-text-o",
            "hidden": false,
            "level": 1,
            "path": "/home",
            "componentUrl": "@/components/Home",
            "keepAlive": false,
            "children": [
                {
                    "id": 5,
                    "name": "articleList",
                    "title": "文章列表",
                    "icon": "",
                    "hidden": false,
                    "level": 2,
                    "path": "/articleList",
                    "componentUrl": "@/components/ArticleList",
                    "keepAlive": true,
                    "children": []
                },
                {
                    "id": 6,
                    "name": "publishArticle",
                    "title": "发表文章",
                    "icon": "",
                    "hidden": false,
                    "level": 2,
                    "path": "/postArticle",
                    "componentUrl": "@/components/PostArticle",
                    "keepAlive": false,
                    "children": []
                },
                {
                    "id": 7,
                    "name": "blogDetail",
                    "title": "博客详情",
                    "icon": "",
                    "hidden": true,
                    "level": 2,
                    "path": "/blogDetail",
                    "componentUrl": "@/components/BlogDetail",
                    "keepAlive": false,
                    "children": []
                },
                {
                    "id": 8,
                    "name": "editBlog",
                    "title": "编辑博客",
                    "icon": "",
                    "hidden": true,
                    "level": 2,
                    "path": "/editBlog",
                    "componentUrl": "@/components/PostArticle",
                    "keepAlive": false,
                    "children": []
                }
            ]
        },
        {
            "id": 2,
            "name": "userManagement",
            "title": "用户管理",
            "icon": "fa fa-user-o",
            "hidden": false,
            "level": 1,
            "path": "/home",
            "componentUrl": "@/components/Home",
            "keepAlive": false,
            "children": [
                {
                    "id": 9,
                    "name": "userManagement",
                    "title": "用户管理",
                    "icon": "fa fa-user-o",
                    "hidden": false,
                    "level": 2,
                    "path": "/user",
                    "componentUrl": "@/components/UserMana",
                    "keepAlive": false,
                    "children": []
                }
            ]
        },
        {
            "id": 3,
            "name": "categoriesManagement",
            "title": "栏目管理",
            "icon": "fa fa-reorder",
            "hidden": false,
            "level": 1,
            "path": "/home",
            "componentUrl": "@/components/Home",
            "keepAlive": false,
            "children": [
                {
                    "id": 10,
                    "name": "cateManagement",
                    "title": "栏目管理",
                    "icon": "fa fa-reorder",
                    "hidden": false,
                    "level": 2,
                    "path": "/cateMana",
                    "componentUrl": "@/components/CateMana",
                    "keepAlive": false,
                    "children": []
                }
            ]
        },
        {
            "id": 4,
            "name": "dataStatics",
            "title": "数据统计",
            "icon": "fa fa-bar-chart",
            "hidden": false,
            "level": 1,
            "path": "/home",
            "componentUrl": "@/components/Home",
            "keepAlive": false,
            "children": [
                {
                    "id": 11,
                    "name": "dataStatics",
                    "title": "数据统计",
                    "icon": "fa fa-bar-chart",
                    "hidden": false,
                    "level": 2,
                    "path": "/charts",
                    "componentUrl": "@/components/DataCharts",
                    "keepAlive": false,
                    "children": []
                }
            ]
        }
    ]
}

4 结束语

本文从后端的角度开发了基于用户角色的页面路由权限控制的接口,主要使用了5张表,分别是user表、roles表、roles_user表、router_resources表和role_resource表,roles表作为一个中间表用来关联资源和用户。在这5张表的基础上开发了一个用于前端根据用户角色展示菜单资源用的查询用户角色下的页面菜单资源接口。

用户的认证沿用了bolgserver项目中的Spring-Security,并在用户认证成功的回调函数中在响应信息中增加写入用户的信息。下一篇文章笔者将结合前端在页面看到基于用户角色控制用户访问菜单权限的效果。接下来几遍文章会写一系列实现从给用户分配角色、给角色授予菜单路由权限到具象到控制按钮操作级别权限的实战文章,敬请期待!

5 往期推荐文章

[1] 介绍一个开源博客项目VBlog并打包部署到已存在运行项目的Nginx服务器下

[2] SpringBoot项目集成阿里云对象存储服务实现文件上传

[3] 改造jeecg-boot项目,解决启动报错,跑通开发环境!

[4] SpringBoot项目中集成第三方登录功能

本文代码以上到个人的gitee仓库:

https://gitee.com/heshengfu1211/blogserver.git

---END---

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 阿福谈Web编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 引言
  • 1 与权限相关的表
    • 1.1 user表
      • 1.2 roles表
        • 1.3 roles_user表
          • 1.4 router_resources(路由资源)表
            • 1.5 role_resources(角色资源)表
              • 五个表之间的关系图
              • 2 根据角色ID查询资源列表接口
                • 2.1 RouterResource与 RouterResourceVo
                  • 2.3 Service层代码
                    • 2.4 Controller层代码
                      • 2.5 修改认证成功回调方法
                      • 3 接口测试
                        • 3.1 登录接口
                          • 3.1 根据角色ID查询路由资源接口
                          • 4 结束语
                          • 5 往期推荐文章
                          相关产品与服务
                          对象存储
                          对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档