I have a Spring 4 MVC application that uses @RestController
annotations for all of the endpoints. The application does not use a web.xml
nor an applicationContext.xml
file. Everything works perfectly fine and now I am trying to get Apache Shiro working in cooperation with everything else.
What I would like to do is have Shiro throw an exception when there is an authc/authz problem accessing any of the REST services that I have built.
So far I was able to set up a basic class set up for initializing Shiro:
@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; } }
When I build an endpoint like this with @RequiresUser
and set breakpoints in the AuthorizingRealm
in the initializer, nothing seems to picked up and the message displays without kicking off any authc/authz functionality:
@RestController @RequestMapping (value = "/") @RequiresUser public class RootEndpoint { @RequestMapping (method = RequestMethod.GET) public String doGet() throws Exception { return "{ \"hello\": \"world\" }"; } }
So here are my questions
- Q1 What am I missing in my configuration? Keep in mind I am trying to avoid the XML configurations if at all possible.
- Q2 After getting the basics, what can I use to throw exceptions instead of redirect the user to another page? Do I need a custom
ShiroFilterFactoryBean
of my own?
1 Answers
Answers 1
I was able to get this to work by actually switching to Spring Boot, which I was recently permitted to do by the owner of the code. I finally wound up with a configuration class for Shiro that looked like this:
@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; } }
I also added a custom handler to use exceptions and custom return data by adding a @ControllerAdvice
implementation that contained:
@ExceptionHandler ({ AuthenticationException.class, UnknownAccountException.class, UnauthenticatedException.class, IncorrectCredentialsException.class, UnauthorizedException.class} ) @ResponseStatus (value = HttpStatus.UNAUTHORIZED) @ResponseBody public RestApiErrorResponse handleUnauthorized() { return build(HttpStatus.UNAUTHORIZED); }
0 comments:
Post a Comment