数据库相关信息

要想对角色权限进行管理的话,至少需要五张表,无论是一个人拥有一个角色,或者多个角色,都需要五张表
分别是
用户表,角色表,用户角色表,权限表,角色权限表
具体表如下,这里偷懒,不加外键了

shiroMysql.png

ShiroConfig

重中之重的 shiro配置如下:

  1. @Configuration
  2. public class ShiroConfig {
  3. /**
  4. * ShiroFilterFactoryBean 处理拦截资源文件问题。
  5. * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
  6. * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
  7. *
  8. * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
  9. * 3、部分过滤器可指定参数,如perms,roles
  10. *
  11. */
  12. @Bean(name = "shiroFilterFactoryBean")
  13. public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
  14. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  15. // 必须设置 SecurityManager
  16. shiroFilterFactoryBean.setSecurityManager(securityManager);
  17. // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
  18. shiroFilterFactoryBean.setLoginUrl("/login.html");
  19. shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth.html");
  20. // 拦截器.
  21. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
  22. // 配置不会被拦截的链接 顺序判断
  23. filterChainDefinitionMap.put("/static/**", "anon");
  24. filterChainDefinitionMap.put("/user/login", "anon");
  25. // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
  26. filterChainDefinitionMap.put("/logout", "logout");
  27. // 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 :这是一个坑呢,一不小心代码就不好使了;
  28. // ① authc:所有url都必须认证通过才可以访问; ② anon:所有url都都可以匿名访问
  29. filterChainDefinitionMap.put("/**", "authc");
  30. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
  31. return shiroFilterFactoryBean;
  32. }
  33. @Bean(name = "securityManager")
  34. public DefaultWebSecurityManager securityManager() {
  35. DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  36. // 设置realm.
  37. securityManager.setRealm(myRealm());
  38. return securityManager;
  39. }
  40. /**
  41. * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
  42. *
  43. * @return
  44. */
  45. @Bean
  46. public MyRealm myRealm() {
  47. MyRealm myRealm = new MyRealm();
  48. return myRealm;
  49. }
  50. /**
  51. * Shiro生命周期处理器
  52. * @return
  53. */
  54. @Bean
  55. public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
  56. return new LifecycleBeanPostProcessor();
  57. }
  58. /**
  59. * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
  60. * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
  61. * @return
  62. */
  63. @Bean
  64. @DependsOn({ "lifecycleBeanPostProcessor" })
  65. public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
  66. DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
  67. advisorAutoProxyCreator.setProxyTargetClass(true);
  68. return advisorAutoProxyCreator;
  69. }
  70. @Bean
  71. public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
  72. AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
  73. authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
  74. return authorizationAttributeSourceAdvisor;
  75. }
  76. }

编写认证和授权逻辑

  1. public class MyRealm extends AuthorizingRealm {
  2. @Autowired
  3. private UserDao userDao;
  4. @Autowired
  5. private RoleDao roleDao;
  6. @Autowired
  7. private PermissionDao permissionDao;
  8. /**
  9. * 授权
  10. */
  11. @Override
  12. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  13. String userName=(String) SecurityUtils.getSubject().getPrincipal();
  14. SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
  15. Set<String> roles=new HashSet<String>();
  16. List<Role> rolesByUserName = roleDao.getRolesByUserName(userName);
  17. rolesByUserName
  18. .stream()
  19. .forEach((role) -> {
  20. roles.add(role.getRoleName());
  21. });
  22. List<Permission> permissionsByUserName = permissionDao.getPermissionsByUserName(userName);
  23. permissionsByUserName
  24. .stream()
  25. .forEach((permission) ->{
  26. info.addStringPermission(permission.getPermissionName());
  27. });
  28. info.setRoles(roles);
  29. return info;
  30. }
  31. /**
  32. * 认证
  33. */
  34. @Override
  35. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  36. System.out.println("token.getPrincipal:" + token.getPrincipal());
  37. System.out.println("token.getCredentials:" + token.getCredentials());
  38. String userName = token.getPrincipal().toString();
  39. User user = userDao.getUserByUserName(userName);
  40. if (user != null) {
  41. //说明用户存在
  42. // Object principal, Object credentials, String realmName
  43. AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
  44. return authcInfo;
  45. } else {
  46. //否则认证失败
  47. return null;
  48. }
  49. }
  50. }

UserController

具体的相关接口如下

@RestController
@RequestMapping("/user")
public class UserController {

    @PostMapping("/login")
    public Map<String, Object> login(@Valid User user, BindingResult bindingResult, HttpSession session) {
        Map<String, Object> map = new HashMap<String, Object>();

        // 1、JSR303
        if (bindingResult.hasErrors()) {
            map.put("success", false);
            map.put("errorInfo", bindingResult.getFieldError().getDefaultMessage());
            return map;
        }

        // 2、Shiro
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try {
            subject.login(token);
            map.put("success", true);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            map.put("success", false);
            map.put("errorInfo", "用户名或者密码错误!");
            return map;
        }
    }

    @RequiresPermissions({"select"}) //没有的话 AuthorizationException
    @PostMapping("/select")
    public Map<String, Object> selectPermission() {
        System.out.println("select");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前角色有查看的权力");
        return map;
    }

    @RequiresPermissions({"insert"}) //没有的话 AuthorizationException
    @PostMapping("/insert")
    public Map<String, Object> insertPermission() {
        System.out.println("insert");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前角色有增加的权力");
        return map;
    }

    @RequiresPermissions({"update"}) //没有的话 AuthorizationException
    @PostMapping("/update")
    public Map<String, Object> updatePermission() {
        System.out.println("update");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前角色有更新的权力");
        return map;
    }

    @RequiresPermissions({"delete"}) //没有的话 AuthorizationException
    @PostMapping("/delete")
    public Map<String, Object> deletePermission() {
        System.out.println("delete");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前角色有删除的权力");
        return map;
    }

    @RequiresRoles({"vip"}) //没有的话 AuthorizationException
    @PostMapping("/vip")
    public Map<String, Object> vipRole() {
        System.out.println("vip");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前用户具有 vip 角色");
        return map;
    }

    @RequiresRoles({"ip"}) //没有的话 AuthorizationException
    @PostMapping("/ip")
    public Map<String, Object> ipRole() {
        System.out.println("ip");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前用户具有 ip 角色");
        return map;
    }

    @RequiresRoles({"p"}) //没有的话 AuthorizationException
    @PostMapping("/p")
    public Map<String, Object> pRole() {
        System.out.println("vip");
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        map.put("msg", "当前用户具有 p 角色");
        return map;
    }
}

对AuthorizationException进行处理

如果没有权限访问资源,控制台会报错,而且会直接返给前台错误,看着十分不舒服。
于是对错误进行了一些处理

@ControllerAdvice
public class NoPermissionException {

    @ResponseBody
    @ExceptionHandler(UnauthorizedException.class)
    public String handleShiroException(Exception ex) {
        return "无权限";
    }

    @ResponseBody
    @ExceptionHandler(AuthorizationException.class)
    public String AuthorizationException(Exception ex) {
        return "权限认证失败";
    }
}