我加入阿帕奇四郎到我的應用程序,我想知道,如果出現以下錯誤消息是真正準確:Shiro中未綁定的SecurityManager真的是無效的應用程序配置嗎?
org.apache.shiro.UnavailableSecurityManagerException:沒有安全管理器訪問到調用代碼,無論是綁定到org.apache.shiro.util.ThreadContext或者作爲一個vm靜態單例。這是一個無效的應用程序配置。
我已經通過源代碼中的位和的印象,我得到的是,只要我不使用SecurityUtils
,我願意通過一個SecurityManager
到需要它,我不看了組件實際上不需要將SecurityManager
分配給SecurityUtils
使用的靜態單例。
我想避免的具體事情是讓Shiro將任何東西放入ThreadLocal
或讓Shiro使用它的ThreadContext
支持類。我正在使用Apache Thrift,不想讓自己參與每個請求的單線程網絡設計。我對Shiro的要求很少,所以我會在下面展示我正在做的事情。
我使用吉斯在我的應用程序,但我用shiro-guice
我不因爲四郎AOP的東西依賴於具有與ThreadContext
相關的Subject
。相反,我從一個非常簡單的Guice模塊開始。
public class ShiroIniModule extends AbstractModule {
@Override
protected void configure() {}
@Provides
@Singleton
public SecurityManager provideSecurityManager() {
return new DefaultSecurityManager(new IniRealm("classpath:shiro.ini"));
}
}
這不完全是一個生產質量領域/安全管理器設置,但它足以讓我測試。接下來,我創建了我自己的管理員類,其範圍非常有限,我的應用程序組件使用它。我有兩個;一個ThriftAuthenticationManager
和一個ThriftAuthorizationManager
。這裏是前者:
@Singleton
public class ThriftAuthenticationManager {
private final Logger log = LoggerFactory.getLogger(ThriftAuthenticationManager.class);
private final SecurityManager securityManager;
@Inject
public ThriftAuthenticationManager(SecurityManager securityManager) {
this.securityManager = securityManager;
}
public String authenticate(String username, String password) throws TException {
try {
Subject currentUser = new Subject.Builder(securityManager).buildSubject();
if (!currentUser.isAuthenticated()) {
currentUser.login(new UsernamePasswordToken(username, password));
}
String authToken = currentUser.getSession().getId().toString();
Preconditions.checkState(!Strings.isNullOrEmpty(authToken));
return authToken;
}
catch (AuthenticationException e) {
throw Exceptions.security(SecurityExceptions.AUTHENTICATION_EXCEPTION);
}
catch(Throwable t) {
log.error("Unexpected error during authentication.", t);
throw new TException("Unexpected error during authentication.", t);
}
}
}
而後者:
@Singleton
public class ThriftAuthorizationManager {
private final Logger log = LoggerFactory.getLogger(ThriftAuthorizationManager.class);
private final SecurityManager securityManager;
@Inject
public ThriftAuthorizationManager(SecurityManager securityManager) {
this.securityManager = securityManager;
}
public void checkPermissions(final String authToken, final String permissions)
throws TException {
withThriftExceptions(new Callable<Void>() {
@Override
public Void call() throws Exception {
securityManager.checkPermission(getPrincipals(authToken), permissions);
return null;
}
});
}
public void checkPermission(final String authToken, final Permission permission)
throws TException {
withThriftExceptions(new Callable<Void>() {
@Override
public Void call() throws Exception {
securityManager.checkPermission(getPrincipals(authToken), permission);
return null;
}
});
}
private Subject getSubject(String authToken) {
return new Subject.Builder(securityManager).sessionId(authToken).buildSubject();
}
private PrincipalCollection getPrincipals(String authToken) {
return getSubject(authToken).getPrincipals();
}
private void withThriftExceptions(Callable<Void> callable) throws TException {
try {
callable.call();
}
catch(SessionException e) {
throw Exceptions.security(SecurityExceptions.SESSION_EXCEPTION);
}
catch(UnauthenticatedException e) {
throw Exceptions.security(SecurityExceptions.UNAUTHENTICATED_EXCEPTION);
}
catch(AuthorizationException e) {
throw Exceptions.security(SecurityExceptions.AUTHORIZATION_EXCEPTION);
}
catch(ShiroException e) {
throw Exceptions.security(SecurityExceptions.SECURITY_EXCEPTION);
}
catch(Throwable t) {
log.error("An unexpected error occurred during authorization.", t);
throw new TException("Unexpected error during authorization.", t);
}
}
}
我節儉服務使用上述兩類用於身份驗證和授權。例如:
@Singleton
public class EchoServiceImpl implements EchoService.Iface {
private final Logger log = LoggerFactory.getLogger(EchoServiceImpl.class);
private final ThriftAuthorizationManager authorizor;
@Inject
public EchoServiceImpl(ThriftAuthorizationManager authorizor) {
this.authorizor = authorizor;
}
@Override
public Echo echo(String authToken, Echo echo) throws TException {
authorizor.checkPermissions(authToken, "echo");
return echo;
}
}
所以,我想我其實有幾個任務。
我引用的錯誤實際上是一個錯誤還是僅僅是過度熱情的日誌消息?
如果我從不使用
ShiroUtils
,我需要擔心Shiro依賴ThreadContext
中的任何內容嗎?如果我無法保證單線程每請求的環境,那麼使用
SecurityUtils#setSecurityManager
會有什麼壞處嗎?我還沒有嘗試使用Shiro的高級權限(
org.apache.shiro.authz.Permission
)呢。他們是否依賴ThreadContext
中的任何東西,或做任何奇怪的事情,我應該早點看看?我做了其他任何可能會導致我出現問題的事情,還是我可以改進任何事情?