問題背景
在實現多Realm時,擴展了ModularRealmAuthenticator 和 UsernamePasswordToken,於是在MyAuthenticationToken token = (MyAuthenticationToken) authenticationToken時出現了轉型異常。
擴展ModularRealmAuthenticator 的代碼如下:
public class MyModularRealmAuthenticator extends ModularRealmAuthenticator { @Override public AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { MyAuthenticationToken token = (MyAuthenticationToken) authenticationToken; String loginType = token.getLoginType(); Collection<Realm> realms = getRealms(); Collection<Realm> authRealms = new ArrayList<>(); for(Realm realm : realms){ if(realm.getName().equals(loginType)){ authRealms.add(realm); } } if(authRealms.size() == 1){ return doSingleRealmAuthentication(authRealms.iterator().next(), token); } else { return doMultiRealmAuthentication(authRealms, token); } } }
擴展UsernamePasswordToken的代碼如下:
public class MyAuthenticationToken extends UsernamePasswordToken { private String loginType; public MyAuthenticationToken(final String username, final String password, String loginType){ super(username, password); this.loginType = loginType; } public String getLoginType() { return loginType; } public void setLoginType(String loginType) { this.loginType = loginType; } }
從上面的擴展中我們看到,沒有地方使用我們的自己擴展的MyAuthenticationToken類,只是轉型而已,並沒有去實例化它,這才是引起問題的根源,問題發現了要如何解決它呢?我們在配置的使用並並未制定表單過濾器,此時默認使用的是FormAuthenticationFilter,而Token默認使用的也是UsernamePasswordToken,由此看來,我們擴展的MyAuthenticationToken就成了擺設,此時就要想辦法如何將我們的擴展引用到程序里去呢?
既然默認的是UsernamePasswordToken,那么是如何將UsernamePasswordToken注入的呢?我們進入默認的Filter(FormAuthenticationFilter)可以看到createToken,這個是創建Token的,那么似乎可以和我們的Token搭上邊了,FormAuthenticationFilter中的createToken如下:
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { String username = this.getUsername(request); String password = this.getPassword(request); return this.createToken(username, password, request, response); }
我們發現期間又調用了createToken,此createToken為父類的方法,我們可以看到FormAuthenticationFilter的聲明為 public class FormAuthenticationFilter extends AuthenticatingFilter ,也即內部調用的token為AuthenticatingFilter的實現,繼續看AuthenticatingFilter中的createToken實現,而createToken中又調用了自身createToken的重載,源碼如下:
protected AuthenticationToken createToken(String username, String password, ServletRequest request, ServletResponse response) { boolean rememberMe = this.isRememberMe(request); String host = this.getHost(request); return this.createToken(username, password, rememberMe, host); } protected AuthenticationToken createToken(String username, String password, boolean rememberMe, String host) { return new UsernamePasswordToken(username, password, rememberMe, host); }
可以看到在重載的createToken中,UsernamePasswordToken終於現身了,那么此時,我們可以通過擴展FormAuthenticationFilter並重新createToken將我們擴展的Token引入即可,以下為擴展的FormAuthenticationFilter:
public class MyFormAuthenticationFilter extends FormAuthenticationFilter { @Override protected MyAuthenticationToken createToken(ServletRequest request, ServletResponse response) { String username = getUsername(request); String password = getPassword(request); String loginType = request.getParameter("loginType"); if("sys".equals(loginType)){ return new MyAuthenticationToken(username, password, "sys"); } else { return new MyAuthenticationToken(username, password, "wx"); } } }
擴展完成后,需要配置默認的過濾器為我們的擴展,shiro的配置如下:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/index"/> <property name="unauthorizedUrl" value="/403"/> <property name="filterChainDefinitions"> <value> /favicon.ico = anon /logout = logout /** = authc </value> </property> <property name="filters"> <map> <entry key="authc" value-ref="myFormAuthenticationFilter" /> </map> </property> </bean> <bean id="myFormAuthenticationFilter" class="com.yuxiao.springboot.springbootmybatis.config.shiro.filter.MyFormAuthenticationFilter"/>