001/* 002 * Copyright 2012 Atteo. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.atteo.moonshine.shiro; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.List; 021 022import javax.inject.Inject; 023import javax.servlet.FilterChain; 024import javax.servlet.ServletRequest; 025import javax.servlet.ServletResponse; 026import javax.xml.bind.annotation.XmlElement; 027import javax.xml.bind.annotation.XmlElementRef; 028import javax.xml.bind.annotation.XmlElementWrapper; 029import javax.xml.bind.annotation.XmlRootElement; 030 031import org.apache.shiro.SecurityUtils; 032import org.apache.shiro.authz.annotation.RequiresGuest; 033import org.apache.shiro.authz.annotation.RequiresPermissions; 034import org.apache.shiro.authz.annotation.RequiresUser; 035import org.apache.shiro.guice.ShiroModule; 036import org.apache.shiro.guice.aop.ShiroAopModule; 037import org.apache.shiro.guice.web.GuiceShiroFilter; 038import org.apache.shiro.guice.web.ShiroWebModule; 039import org.apache.shiro.mgt.SecurityManager; 040import org.apache.shiro.realm.Realm; 041import org.apache.shiro.session.mgt.SessionManager; 042import org.apache.shiro.util.ThreadContext; 043import org.apache.shiro.web.filter.mgt.FilterChainResolver; 044import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 045import org.apache.shiro.web.mgt.WebSecurityManager; 046import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; 047import org.atteo.config.XmlDefaultValue; 048import org.atteo.moonshine.TopLevelService; 049import org.atteo.moonshine.services.Service; 050 051import com.google.inject.Key; 052import com.google.inject.Module; 053import com.google.inject.PrivateModule; 054import com.google.inject.binder.AnnotatedBindingBuilder; 055import com.google.inject.multibindings.Multibinder; 056import com.google.inject.name.Names; 057 058/** 059 * ShiroService service. 060 * 061 * <p> 062 * Binds {@link SecurityManager}. 063 * </p> 064 */ 065@XmlRootElement(name = "shiro") 066public class ShiroService extends TopLevelService { 067 068 /** 069 * Enables Shiro AOP functionality. 070 * 071 * <p> 072 * With Shiro AOP you can use annotations for permission checking: 073 * {@link RequiresPermissions}, {@link RequiresUser}, {@link RequiresGuest}, etc. 074 * </p> 075 */ 076 @XmlElement 077 @XmlDefaultValue("true") 078 private Boolean aop; 079 080 @XmlElementWrapper(name = "realms") 081 @XmlElementRef 082 private List<RealmService> realms = new ArrayList<>(); 083 084 /** 085 * URL prefix to filter through ShiroService. 086 */ 087 @XmlElement 088 private String prefix = "/*"; 089 090 @Override 091 public Iterable<? extends Service> getSubServices() { 092 return realms; 093 } 094 095 @Override 096 public Module configure() { 097 return new PrivateModule() { 098 @Override 099 protected void configure() { 100 install(new ShiroModule() { 101 @Override 102 protected void configureShiro() { 103 Multibinder<Realm> setBinder = Multibinder.newSetBinder(binder(), Realm.class); 104 for (RealmService realm : realms) { 105 if (realm.getId() == null) { 106 setBinder.addBinding().to(Realm.class); 107 } else { 108 setBinder.addBinding().to(Key.get(Realm.class, Names.named(realm.getId()))); 109 } 110 111 } 112 113 try { 114 // Guice will initialize manager with list of realms 115 bind(WebSecurityManager.class).toConstructor( 116 DefaultWebSecurityManager.class.getConstructor(Collection.class)) 117 .asEagerSingleton(); 118 } catch (NoSuchMethodException e) { 119 addError(e); 120 } 121 expose(WebSecurityManager.class); 122 } 123 124 @Override 125 protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind) { 126 // make configurable 127 bind.to(DefaultWebSessionManager.class).asEagerSingleton(); 128 } 129 }); 130 FilterChainResolver filterChainResolver = new FilterChainResolver() { 131 @Override 132 public FilterChain getChain(ServletRequest request, ServletResponse response, 133 FilterChain chain) { 134 return null; 135 } 136 }; 137 bind(FilterChainResolver.class).toInstance(filterChainResolver); 138 139 bind(GuiceShiroFilter.class).asEagerSingleton(); 140 141 install(ShiroWebModule.guiceFilterModule(prefix)); 142 if (aop) { 143 install(new ShiroAopModule()); 144 } 145 146 expose(SecurityManager.class); 147 expose(WebSecurityManager.class); 148 } 149 }; 150 } 151 152 @Inject 153 private SecurityManager securityManager; 154 155 @Override 156 public void start() { 157 SecurityUtils.setSecurityManager(securityManager); 158 } 159 160 @Override 161 public void close() { 162 SecurityUtils.setSecurityManager(null); 163 164 ThreadContext.unbindSecurityManager(); 165 ThreadContext.unbindSubject(); 166 } 167}