上一篇文章,实现了用户验证 查看,接下来实现下权限控制

权限控制,是管理资源访问的过程,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等

Apache Shiro 通过继承AuthorizingRealm自定义实现ShiroRealm类,实现 doGetAuthenticationInfo()方法完成用户认证,实现doGetAuthorizationInfo()方法完成权限控制

ShiroRealm 涉及到:   principal:主体,就是登陆的当前用户类型的数据实体   credentials:凭证,用户的密码,具体加密方式用户自己实现,什么都不做就是原文

1.数据库

shiro本身只提供拦截路由,具体的数据源则由用户自己提供

使用RBAC(Role-Based Access Control,基于角色的访问控制)设计用户,角色和权限间的关系

表结构

用户表user

CREATE TABLE `user` (

`id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,

`account` varchar(64) NOT NULL COMMENT '账号',

`password` char(32) NOT NULL COMMENT '密码',

`email` varchar(50) NOT NULL COMMENT '邮箱',

`status` tinyint(1) DEFAULT '0' COMMENT '状态 1-正常,0-禁用,-1-删除',

`create_time` int(11) unsigned NOT NULL COMMENT '添加时间',

`last_login_time` int(11) unsigned DEFAULT '0' COMMENT '上次登陆时间',

`last_login_ip` varchar(40) DEFAULT NULL COMMENT '上次登录IP',

`login_count` mediumint(8) unsigned DEFAULT '0' COMMENT '登陆次数',

PRIMARY KEY (`id`),

UNIQUE KEY `account` (`account`)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='管理员';

-- ----------------------------

-- Records of user

-- ----------------------------

INSERT INTO `user` VALUES ('1', 'super', 'ba98ee766e18d8352c9ad4add8387d54', 'z11qq118@126.com', '1', '0', '1554273147', '2887450713', '223');

INSERT INTO `user` VALUES ('2', 'superadmin', 'e2dfe8256580c9d514863979f86b43b6', 'z11z13@126.com1', '1', '1496993690', '1496993732', '127.0.0.1', '0');

INSERT INTO `user` VALUES ('3', 'manager', '0da1810a78a0a6134d4535b995df8e89', '123@qq.com', '1', '1530083227', '1542873068', '2130706433', '9');

角色表role

CREATE TABLE `role` (

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

`name` varchar(100) DEFAULT NULL COMMENT '角色名称',

`memo` varchar(100) DEFAULT NULL COMMENT '角色描述',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------

-- Records of role

-- ----------------------------

INSERT INTO `role` VALUES ('1', 'admin', '超级管理员');

INSERT INTO `role` VALUES ('2', 'test', '测试账户');

用户角色关联表user_role

CREATE TABLE `user_role` (

`uid` int(10) DEFAULT NULL COMMENT '用户id',

`rid` int(10) DEFAULT NULL COMMENT '角色id'

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------

-- Records of user_role

-- ----------------------------

INSERT INTO `user_role` VALUES ('1', '1');

INSERT INTO `user_role` VALUES ('3', '2');

权限表permission

CREATE TABLE `permission` (

`id` int(10) NOT NULL,

`url` varchar(255) DEFAULT NULL COMMENT 'url地址',

`name` varchar(100) DEFAULT NULL COMMENT 'url描述',

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------

-- Records of permission

-- ----------------------------

INSERT INTO `permission` VALUES ('1', '/user', 'user:user');

INSERT INTO `permission` VALUES ('2', '/user/add', 'user:add');

INSERT INTO `permission` VALUES ('3', '/user/delete', 'user:delete');

权限角色关联表role_permission

CREATE TABLE `role_permission` (

`rid` int(10) DEFAULT NULL COMMENT '角色id',

`pid` int(10) DEFAULT NULL COMMENT '权限id'

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------

-- Records of role_permission

-- ----------------------------

INSERT INTO `role_permission` VALUES ('1', '2');

INSERT INTO `role_permission` VALUES ('1', '3');

INSERT INTO `role_permission` VALUES ('2', '1');

INSERT INTO `role_permission` VALUES ('1', '1');

2.数据层

修改mybatis-generator.xml中的tableName和domainObjectName,自动生成上面上面表的相关代码再进行添加或修改

  (1)User

    User

package com.sfn.bms.system.model;

import java.io.Serializable;

import javax.persistence.*;

public class User implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Short id;

/**

* 账号

*/

private String account;

/**

* 密码

*/

private String password;

/**

* 邮箱

*/

private String email;

/**

* 状态 1-正常,0-禁用,-1-删除

*/

private Boolean status;

/**

* 添加时间

*/

@Column(name = "create_time")

private Integer createTime;

/**

* 上次登陆时间

*/

@Column(name = "last_login_time")

private Integer lastLoginTime;

/**

* 上次登录IP

*/

@Column(name = "last_login_ip")

private String lastLoginIp;

/**

* 登陆次数

*/

@Column(name = "login_count")

private Integer loginCount;

private static final long serialVersionUID = 1L;

/**

* @return id

*/

public Short getId() {

return id;

}

/**

* @param id

*/

public void setId(Short id) {

this.id = id;

}

/**

* 获取账号

*

* @return account - 账号

*/

public String getAccount() {

return account;

}

/**

* 设置账号

*

* @param account 账号

*/

public void setAccount(String account) {

this.account = account == null ? null : account.trim();

}

/**

* 获取密码

*

* @return password - 密码

*/

public String getPassword() {

return password;

}

/**

* 设置密码

*

* @param password 密码

*/

public void setPassword(String password) {

this.password = password == null ? null : password.trim();

}

/**

* 获取邮箱

*

* @return email - 邮箱

*/

public String getEmail() {

return email;

}

/**

* 设置邮箱

*

* @param email 邮箱

*/

public void setEmail(String email) {

this.email = email == null ? null : email.trim();

}

/**

* 获取状态 1-正常,0-禁用,-1-删除

*

* @return status - 状态 1-正常,0-禁用,-1-删除

*/

public Boolean getStatus() {

return status;

}

/**

* 设置状态 1-正常,0-禁用,-1-删除

*

* @param status 状态 1-正常,0-禁用,-1-删除

*/

public void setStatus(Boolean status) {

this.status = status;

}

/**

* 获取添加时间

*

* @return create_time - 添加时间

*/

public Integer getCreateTime() {

return createTime;

}

/**

* 设置添加时间

*

* @param createTime 添加时间

*/

public void setCreateTime(Integer createTime) {

this.createTime = createTime;

}

/**

* 获取上次登陆时间

*

* @return last_login_time - 上次登陆时间

*/

public Integer getLastLoginTime() {

return lastLoginTime;

}

/**

* 设置上次登陆时间

*

* @param lastLoginTime 上次登陆时间

*/

public void setLastLoginTime(Integer lastLoginTime) {

this.lastLoginTime = lastLoginTime;

}

/**

* 获取上次登录IP

*

* @return last_login_ip - 上次登录IP

*/

public String getLastLoginIp() {

return lastLoginIp;

}

/**

* 设置上次登录IP

*

* @param lastLoginIp 上次登录IP

*/

public void setLastLoginIp(String lastLoginIp) {

this.lastLoginIp = lastLoginIp == null ? null : lastLoginIp.trim();

}

/**

* 获取登陆次数

*

* @return login_count - 登陆次数

*/

public Integer getLoginCount() {

return loginCount;

}

/**

* 设置登陆次数

*

* @param loginCount 登陆次数

*/

public void setLoginCount(Integer loginCount) {

this.loginCount = loginCount;

}

}

View Code

    UserMapper

package com.sfn.bms.system.mapper;

import com.sfn.bms.common.config.MyMapper;

import com.sfn.bms.system.model.User;

public interface UserMapper extends MyMapper {

}

View Code

    UserService(新增)

package com.sfn.bms.system.service;

import com.sfn.bms.common.service.IService;

import com.sfn.bms.system.model.User;

public interface UserService extends IService {

User findByAccount(String account);

}

View Code

    UserServiceImpl (新增)

package com.sfn.bms.system.service.impl;

import com.sfn.bms.common.service.impl.BaseService;

import com.sfn.bms.system.model.User;

import com.sfn.bms.system.service.UserService;

import org.springframework.stereotype.Repository;

import tk.mybatis.mapper.entity.Example;

import java.util.List;

@Repository("userService")

public class UserServiceImpl extends BaseService implements UserService {

@Override

public User findByAccount(String account) {

Example example = new Example(User.class);

example.createCriteria().andCondition("lower(account)=", account.toLowerCase());

List list = this.selectByExample(example);

return list.isEmpty() ? null : list.get(0);

}

}

View Code

    UserMapper.xml

View Code

  (2)Role

    Role 

package com.sfn.bms.system.model;

import java.io.Serializable;

import javax.persistence.*;

public class Role implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

/**

* 角色名称

*/

private String name;

/**

* 角色描述

*/

private String memo;

private static final long serialVersionUID = 1L;

/**

* @return id

*/

public Integer getId() {

return id;

}

/**

* @param id

*/

public void setId(Integer id) {

this.id = id;

}

/**

* 获取角色名称

*

* @return name - 角色名称

*/

public String getName() {

return name;

}

/**

* 设置角色名称

*

* @param name 角色名称

*/

public void setName(String name) {

this.name = name == null ? null : name.trim();

}

/**

* 获取角色描述

*

* @return memo - 角色描述

*/

public String getMemo() {

return memo;

}

/**

* 设置角色描述

*

* @param memo 角色描述

*/

public void setMemo(String memo) {

this.memo = memo == null ? null : memo.trim();

}

}

View Code

    RoleMapper(修改)

package com.sfn.bms.system.mapper;

import com.sfn.bms.common.config.MyMapper;

import com.sfn.bms.system.model.Role;

import java.util.List;

public interface RoleMapper extends MyMapper {

List findUserRole(String account);

}

View Code

    RoleService (新增)

package com.sfn.bms.system.service;

import com.sfn.bms.common.service.IService;

import com.sfn.bms.system.model.Role;

import java.util.List;

public interface RoleService extends IService {

List findUserRole(String account);

}

View Code

    RoleServiceImpl (新增)

package com.sfn.bms.system.service.impl;

import com.sfn.bms.common.service.impl.BaseService;

import com.sfn.bms.system.mapper.RoleMapper;

import com.sfn.bms.system.model.Role;

import com.sfn.bms.system.service.RoleService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Propagation;

import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service("RoleService")

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)

public class RoleServiceImpl extends BaseService implements RoleService {

@Autowired

private RoleMapper mapper;

@Override

public List findUserRole(String account) {

return this.mapper.findUserRole(account);

}

}

View Code

    RoleMapper.xml(修改)

View Code

  (3)Permission    

    Permission 

package com.sfn.bms.system.model;

import java.io.Serializable;

import javax.persistence.*;

public class Permission implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

/**

* url地址

*/

private String url;

/**

* url描述

*/

private String name;

private static final long serialVersionUID = 1L;

/**

* @return id

*/

public Integer getId() {

return id;

}

/**

* @param id

*/

public void setId(Integer id) {

this.id = id;

}

/**

* 获取url地址

*

* @return url - url地址

*/

public String getUrl() {

return url;

}

/**

* 设置url地址

*

* @param url url地址

*/

public void setUrl(String url) {

this.url = url == null ? null : url.trim();

}

/**

* 获取url描述

*

* @return name - url描述

*/

public String getName() {

return name;

}

/**

* 设置url描述

*

* @param name url描述

*/

public void setName(String name) {

this.name = name == null ? null : name.trim();

}

}

View Code

    PermissionMapper(修改)

package com.sfn.bms.system.mapper;

import com.sfn.bms.common.config.MyMapper;

import com.sfn.bms.system.model.Permission;

import java.util.List;

public interface PermissionMapper extends MyMapper {

List findUserPermissions(String account);

}

View Code

    PermissionService (新增)

package com.sfn.bms.system.service;

import com.sfn.bms.common.service.IService;

import com.sfn.bms.system.model.Permission;

import java.util.List;

public interface PermissionService extends IService {

List findUserPermissions(String account);

}

View Code

    PermissionServiceImpl (新增)

package com.sfn.bms.system.service.impl;

import com.sfn.bms.common.service.impl.BaseService;

import com.sfn.bms.system.mapper.PermissionMapper;

import com.sfn.bms.system.model.Permission;

import com.sfn.bms.system.service.PermissionService;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Propagation;

import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service("PermissionService")

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)

public class PermissionServiceImpl extends BaseService implements PermissionService {

@Autowired

private PermissionMapper mapper;

@Override

public List findUserPermissions(String account) {

return this.mapper.findUserPermissions(account);

}

}

View Code

    PermissionMapper.xml(修改)

View Code

  (4)UserRole

    UserRole 

package com.sfn.bms.system.model;

import java.io.Serializable;

import javax.persistence.*;

@Table(name = "user_role")

public class UserRole implements Serializable {

/**

* 用户id

*/

private Integer uid;

/**

* 角色id

*/

private Integer rid;

private static final long serialVersionUID = 1L;

/**

* 获取用户id

*

* @return uid - 用户id

*/

public Integer getUid() {

return uid;

}

/**

* 设置用户id

*

* @param uid 用户id

*/

public void setUid(Integer uid) {

this.uid = uid;

}

/**

* 获取角色id

*

* @return rid - 角色id

*/

public Integer getRid() {

return rid;

}

/**

* 设置角色id

*

* @param rid 角色id

*/

public void setRid(Integer rid) {

this.rid = rid;

}

}

View Code

    UserRoleMapper

package com.sfn.bms.system.mapper;

import com.sfn.bms.common.config.MyMapper;

import com.sfn.bms.system.model.UserRole;

public interface UserRoleMapper extends MyMapper {

}

View Code

    UserRoleMapper.xml

View Code

  (5)RolePermission  

    RolePermission 

package com.sfn.bms.system.model;

import java.io.Serializable;

import javax.persistence.*;

@Table(name = "role_permission")

public class RolePermission implements Serializable {

/**

* 角色id

*/

private Integer rid;

/**

* 权限id

*/

private Integer pid;

private static final long serialVersionUID = 1L;

/**

* 获取角色id

*

* @return rid - 角色id

*/

public Integer getRid() {

return rid;

}

/**

* 设置角色id

*

* @param rid 角色id

*/

public void setRid(Integer rid) {

this.rid = rid;

}

/**

* 获取权限id

*

* @return pid - 权限id

*/

public Integer getPid() {

return pid;

}

/**

* 设置权限id

*

* @param pid 权限id

*/

public void setPid(Integer pid) {

this.pid = pid;

}

}

View Code

  RolePermissionMapper

package com.sfn.bms.system.mapper;

import com.sfn.bms.common.config.MyMapper;

import com.sfn.bms.system.model.RolePermission;

public interface RolePermissionMapper extends MyMapper {

}

View Code

  RolePermissionMapper.xml

View Code

3.配置shiro相关文件

  (1)Realm

package com.sfn.bms.common.shiro;

import com.sfn.bms.system.model.Permission;

import com.sfn.bms.system.model.Role;

import com.sfn.bms.system.model.User;

import com.sfn.bms.system.service.PermissionService;

import com.sfn.bms.system.service.RoleService;

import com.sfn.bms.system.service.UserService;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.*;

import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;

import org.apache.shiro.realm.AuthorizingRealm;

import org.apache.shiro.subject.PrincipalCollection;

import org.apache.shiro.subject.SimplePrincipalCollection;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

import java.util.stream.Collectors;

/**

* 自定义实现 ShiroRealm,包含认证和授权两大模块

*/

@Component("shiroRealm")

public class MyShiroRealm extends AuthorizingRealm {

@Autowired

private UserService userService;

@Autowired

private RoleService roleService;

@Autowired

private PermissionService permissionService;

/**

* 授权模块,获取用户角色和权限

*

* @param principal principal

* @return AuthorizationInfo 权限信息

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {

User user = (User) SecurityUtils.getSubject().getPrincipal();

String account = user.getAccount();

System.out.println("用户" + account + "获取权限-----ShiroRealm.doGetAuthorizationInfo");

SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

// 获取用户角色集

List roleList = this.roleService.findUserRole(account);

Set roleSet = roleList.stream().map(Role::getName).collect(Collectors.toSet());

simpleAuthorizationInfo.setRoles(roleSet);

// 获取用户权限集

List permissionList = permissionService.findUserPermissions(account);

Set permissionSet = permissionList.stream().map(Permission::getName).collect(Collectors.toSet());

simpleAuthorizationInfo.setStringPermissions(permissionSet);

return simpleAuthorizationInfo;

}

/**

* 用户认证

*

* @param token AuthenticationToken 身份认证 token

* @return AuthenticationInfo 身份认证信息

* @throws AuthenticationException 认证相关异常

*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

……

}

}

  (2)ShiroConfig

    添加

@Bean

public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){

DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

advisorAutoProxyCreator.setProxyTargetClass(true);

return advisorAutoProxyCreator;

}

@Bean

public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {

AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();

authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);

return authorizationAttributeSourceAdvisor;

}

4.使用

  (1)Controller

  添加UserController

package com.sfn.bms.system.controller;

import com.github.pagehelper.PageHelper;

import com.github.pagehelper.PageInfo;

import com.sfn.bms.system.model.Permission;

import com.sfn.bms.system.model.Role;

import com.sfn.bms.system.model.User;

import com.sfn.bms.system.service.PermissionService;

import com.sfn.bms.system.service.RoleService;

import com.sfn.bms.system.service.UserService;

import org.apache.shiro.authz.annotation.RequiresPermissions;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller

public class UserController {

@Autowired

UserService userService;

@Autowired

private RoleService roleService;

@Autowired

private PermissionService permissionService;

@RequiresPermissions("user:user")

@RequestMapping("user/list")

public String userList(Model model) {

model.addAttribute("value", "获取用户信息");

return "user";

}

@RequiresPermissions("user:add")

@RequestMapping("user/add")

public String userAdd(Model model) {

model.addAttribute("value", "新增用户");

return "user";

}

@RequiresPermissions("user:delete")

@RequestMapping("user/delete")

public String userDelete(Model model) {

model.addAttribute("value", "删除用户");

return "user";

}

}

  在LoginController添加/403跳转

@GetMapping("/403")

public String forbid() {

return "403";

}

  (2)前端页面

  index.html

首页

你好![[${user.account}]]

用户管理

注销

View Code

  user.html

[[${value}]]

[[${value}]]

返回

View Code

  error/403.html

暂无权限

您没有权限访问该资源!!

返回

View Code

5.测试

 启动项目,在登录页输入用户名 manager密码123456,来到主页

数据库中manager属于test角色,没有添加和删除的权限,在跳转到新增用户或删除用户时,页面会被重定向到/403

后台抛出异常org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method

定义一个全局异常捕获类

package com.sfn.bms.common.handler;

import com.sfn.bms.common.domian.ResponseBo;

import com.sfn.bms.common.util.HttpUtils;

import org.apache.shiro.authz.AuthorizationException;

import org.springframework.core.Ordered;

import org.springframework.core.annotation.Order;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.bind.annotation.RestControllerAdvice;

import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

@RestControllerAdvice

@Order(value = Ordered.HIGHEST_PRECEDENCE)

public class GlobalExceptionHandler {

@ExceptionHandler(value = AuthorizationException.class)

public Object handleAuthorizationException(HttpServletRequest request) {

if (HttpUtils.isAjaxRequest(request)) {

return ResponseBo.error("暂无权限,请联系管理员!");

} else {

ModelAndView mav = new ModelAndView();

mav.setViewName("error/403");

return mav;

}

}

}

再次运行项目,登录后选择新增用户,页面成功重定向到/403

 

 相关代码 地址

好文阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。