Spring 4 MVC + Apache Shiro

Spring 4 MVC + Apache Shiro

我有一个 Spring 4 MVC 应用程序,它对所有端点使用 @RestController 注释。该应用程序不使用 web.xmlapplicationContext.xml 文件。一切都运行良好,现在我正在尝试让 Apache Shiro 与其他一切协同工作。

喜欢做的是在访问我构建的任何 REST 服务时出现 authc/authz 问题时让 Shiro 抛出异常。

到目前为止,我已经能够为初始化 Shiro 设置一个基本的 class 设置:

@Configuration
public class ShiroInitializer {

  @Bean(name = "realmLocal")
  @DependsOn ("lifecycleBeanPostProcessor")
  public Realm realmLocal() {
    return new AuthorizingRealm() {
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
      }

      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
      }
    };
  }

  @Bean (name = "lifecycleBeanPostProcessor")
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }

  @Bean
  public org.apache.shiro.mgt.SecurityManager getSecurityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(realmLocal());
    return securityManager;
  }

  @DependsOn("lifecycleBeanPostProcessor")
  @Bean
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
    proxyCreator.setProxyTargetClass(true);
    return proxyCreator;
  }

  // enable shiro annotations
  @Bean
  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(getSecurityManager());
    return advisor;
  }

  @Bean (name = "shiroFilter")
  public ShiroFilterFactoryBean getShiroFilter() {
    ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
    shiroFilter.setSecurityManager(getSecurityManager());

    Map<String, String> filters = new ConcurrentHashMap<>();
    filters.put("/**", "authcBasic");
    shiroFilter.setFilterChainDefinitionMap(filters);

    return shiroFilter;
  }
}

当我使用 @RequiresUser 构建这样的端点并在初始化程序的 AuthorizingRealm 中设置断点时,似乎没有任何结果并且消息显示时没有启动任何 authc/authz功能:

@RestController
@RequestMapping (value = "/")
@RequiresUser
public class RootEndpoint {

  @RequestMapping (method = RequestMethod.GET)
  public String doGet() throws Exception {
    return "{ \"hello\": \"world\" }";
  }
}

这是我的问题

  1. Q1 我的配置中缺少什么?请记住,我正在尽可能避免 XML 配置。
  2. Q2 有了基础之后,我可以用什么来抛出异常而不是将用户重定向到另一个页面?我需要自己定制 ShiroFilterFactoryBean 吗?

我能够通过实际切换到 Spring 引导来实现它,最近代码所有者允许我这样做。我最终为 Shiro 设置了一个配置 class,如下所示:

@Configuration
public class ShiroConfiguration {

  @Bean
  public ShiroFilterFactoryBean getShiroFilter() {
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    factoryBean.setSecurityManager(getSecurityManager());
    return factoryBean;
  }

  @Bean (name = "securityManager")
  public DefaultWebSecurityManager getSecurityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(getLocaleRealm());
    securityManager.setSessionManager(getSessionManager());
    return securityManager;
  }

  @Bean
  public DefaultWebSessionManager getSessionManager() {
    final DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setGlobalSessionTimeout(43200000);
    return sessionManager;
  }

  @Bean (name = "localRealm")
  @DependsOn ("lifecycleBeanPostProcessor")
  public LocalRealm getLocaleRealm() {
    return new LocalRealm();
  }

  @Bean (name = "lifecycleBeanPostProcessor")
  public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }

  @Bean
  public MethodInvokingFactoryBean getMethodInvokingFactoryBean() {
    MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
    factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
    factoryBean.setArguments(new Object[]{getSecurityManager()});
    return factoryBean;
  }
}

我还添加了一个自定义处理程序来使用异常和自定义 return 数据,方法是添加一个 @ControllerAdvice 实现,其中包含:

@ExceptionHandler ({
  AuthenticationException.class,
  UnknownAccountException.class,
  UnauthenticatedException.class,
  IncorrectCredentialsException.class,
  UnauthorizedException.class}
)
@ResponseStatus (value = HttpStatus.UNAUTHORIZED)
@ResponseBody
public RestApiErrorResponse handleUnauthorized() {
  return build(HttpStatus.UNAUTHORIZED);
}