SpringBoot-Shiro权限控制( 3 )

SpringBoot-Shiro权限控制(2019.12.13)

在《Spring-Boot-shiro用户认证》中,我们通过继承AuthorizingRealm抽象类实现了doGetAuthenticationInfo()方法完成了用户认证操作。接下来继续实现doGetAuthorizationInfo()方法完成Shiro的权限控制功能。

授权也称为访问控制,是管理资源访问的过程。即根据不同用户的权限判断其是否有访问相应资源的权限。在Shiro中,权限控制有三个核心的元素:权限,角色和用户。(也称为权限3要素)

库的模型分为 : 用户表 角色表 权限表 (3者都是多对多关系通过中间表关联)

创建了五张表:用户表USER、角色表ROLE、用户角色关联表USER_ROLE、权限表PERMISSION和权限角色关联表ROLE_PERMISSION。用户love角色为admin,用户tester角色为test。admin角色拥有用户的所有权限(user:user,user:add,user:delete),而test角色只拥有用户的查看权限(user:user)。密码都是321,没经过Shiro提供的MD5加密。

1. Dao层

创建两个实体类,对应用户角色表Role和用户权限表Permission:

@Data
public class Role implements Serializable {
    private static final long serialVersionUID = -66L;
    private Integer id;
    private String name;
    private String memo;
}
-----------------------------------------------------------------------------
@Data
public class Permission implements Serializable {
    private static final long serialVersionUID = -66L;
    private Integer id;
    /**
     * 权限路径
     */
    private String url;
    /**
     * 权限名称
     */
    private String name;
}

创建两个dao接口,分别根据用户名查询用户拥有所有角色和用户拥有的所有权限:

UserRoleMapper.java

@Mapper
public interface UserRoleMapper {
    /** 
     * 根据用户名查询用户拥有所有角色 
     *
     * @param username 
     * @return java.util.List<com.zhihao.entity.Role> 
     * @author: zhihao
     * @date: 2019/12/13 
     */
    List<Role> findRoleByUserName(String username);
}

UserRoleMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zhihao.dao.UserRoleMapper">
  <select id="findRoleByUserName" resultType="com.zhihao.entity.Role">
    SELECT ro.* FROM `user` u
    LEFT JOIN user_role uro ON u.id=uro.user_id
    RIGHT JOIN role ro ON ro.id = uro.rid WHERE u.username = #{username}
  </select>
</mapper>

UserPermissionMapper.java

@Mapper
public interface UserPermissionMapper {
    /**
     *  根据用户名 查询出该用户拥有的所有权限
     *
     * @param username 用户名
     * @return java.util.List<com.zhihao.entity.Permission>
     * @author: zhihao
     * @date: 2019/12/13
     * {@link #}
     */
    List<Permission> findPermissionByUserName(String username);
}

UserPermissionMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zhihao.dao.UserPermissionMapper">
  <select id="findPermissionByUserName" resultType="com.zhihao.entity.Permission">
  SELECT per.* FROM `permission` per 
   LEFT JOIN role__permission rope ON per.id=rope.pid 
   LEFT JOIN role ro ON ro.id=rope.rid 
   LEFT JOIN user_role uro ON uro.rid=ro.id 
   LEFT JOIN `user` u ON u.id=uro.user_id WHERE u.username = #{username};
  </select>
</mapper>

2.对ShiroRealm.java进行改造。

在Shiro中,用户角色和权限的获取是在ShiroRealm的doGetAuthorizationInfo()方法中实现的,所以接下来手动实现该方法:

	/**
     * 获取用户角色和权限
     *
     * @param principal
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @author: zhihao
     * @date: 2019/12/13
     * {@link #}
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //通过shiro工具获取登录后的user对象,获取到用户名
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        String username = user.getUsername();
        //创建授权对象进行封装角色和权限信息进去进行返回 注意不是SimpleAuthenticationInfo
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取用户角色集
        List<Role> roleList = roleService.findRoleByUserName(username);
        Set<String> roleSet = new HashSet<>();
        for (Role role : roleList) {
            roleSet.add(role.getName());
        }
        System.out.println("用户拥有的角色>>>"+roleSet);
        //添加角色进角色授权
        info.setRoles(roleSet);
        List<Permission> permissionList = permissionService.findPermissionByUserName(username);
        Set<String> permissionSet = new HashSet<>();
        for (Permission permission : permissionList) {
            permissionSet.add(permission.getName());
        }
        System.out.println("用户拥有的权限>>>"+permissionSet);
        //添加权限进权限授权
        info.setStringPermissions(permissionSet);
        return info;
    }
//..下面是之前的登录认证

在上述代码中,我们通过方法获取了当前登录用户的角色和权限集,然后保存到SimpleAuthorizationInfo对象中,并返回给Shiro,这样Shiro中就存储了当前用户的角色和权限信息了。

3. 对ShiroConfig.java进行改造配置。

要使用权限相关的注解必须要在配置中开启注解的使用,需要在ShiroConfig中添加如下配置:

	/**
     * 开启shiro认证注解
     *
     * @param securityManager
     * @return AuthorizationAttributeSourceAdvisor
     * @author: zhihao
     * @date: 2019/12/13
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

Shiro为我们提供了一些和权限相关的注解,如下所示:

// 表示当前Subject已经通过login进行了身份验证;即Subject.isAuthenticated()返回true。
@RequiresAuthentication  
 
// 表示当前Subject已经身份验证或者通过记住我登录的。
@RequiresUser  

// 表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
@RequiresGuest  

// 表示当前Subject需要角色admin和user。  
@RequiresRoles(value={"admin", "user"}, logical= Logical.AND)  

// 表示当前Subject需要权限user:a或user:b。
@RequiresPermissions (value={"user:a", "user:b"}, logical= Logical.OR)

4. 编写一个权限控制的UserController来测试权限

@RestController
public class UserController {
    /**
     * 模拟从数据库获取了全部用户
     *
     * @return java.lang.String
     * @author: zhihao
     * @date: 2019/12/13
     */
    @RequiresPermissions(value = "user:user") //使用shiro权限注解标明,只能拥有这个user:user权限的用户访问
    @RequestMapping("/list")
    public String getUserList(){
        String userList ="拥有获取全部用户的权限 ``user:user``";
        return userList;
    }

    @RequiresPermissions(value = "user:add")
    @RequestMapping("/add")
    public String addUser(){
        String userList ="拥有添加用户权限 ``user:add``";
        return userList;
    }
    
    @RequiresPermissions(value = "user:delete")
    @RequestMapping("/delete")
    public String deleteUser(){
        String userList ="拥有删除用户权限 ``user:delete``";
        return userList;
    }
}

5. 在LoginController中添加一个/403跳转:

	@GetMapping("/403")
    public ModelAndView forbid() {
        ModelAndView view = new ModelAndView();
        view.setViewName("403");
        return view;
    }

6. 测试没有权限用户后端报AuthorizationException异常,

在测试中发现使用没有权限的用户访问后台抛出了异常:org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method:…异常!!!

本以为在ShiroConfig中配置了shiroFilterFactoryBean.setUnauthorizedUrl("/403");,没有权限的访问会自动重定向到/403,结果证明并不是这样。后来研究发现,该设置只对filterChain起作用,比如在filterChain中设置了filterChainDefinitionMap.put("/user/update", "perms[user:update]");,如果用户没有user:update权限,那么当其访问/user/update的时候,页面会被重定向到/403。

对应以上问题可以有2种解决方法:

第一种: 定义一个全局异常处理进行处理:

@ControllerAdvice
@Order(value = Ordered.HIGHEST_PRECEDENCE) //最高精度,抛出的异常符合这个异常就进来
public class GlobalExceptionHandler {
    @ExceptionHandler(value = AuthorizationException.class)
    public String handleAuthorizationException() {
        return "403";
    }
}

第二种在配置类中配置个异常解析器

	/**
     * 异常解析器
     *
     * @return org.springframework.web.servlet.handler.SimpleMappingExceptionResolver
     * @author: zhihao
     * @date: 2019/12/13
     * {@link #}
     */
	@Bean
    public SimpleMappingExceptionResolver  simpleMappingExceptionResolver(){
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        //拦截到AuthorizationException 就跳转到resources资源下的 如果带文件夹需要加上文件夹: /xxx/403模板页面
        properties.setProperty("AuthorizationException", "/403");
        resolver.setExceptionMappings(properties);
        return resolver;
    }

最后发现无权限访问后正常到403页面了!

扩展资料:

建表语句: 在项目代码中

项目代码(点击打开)

发布了29 篇原创文章 · 获赞 3 · 访问量 1610
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章