实现多Realm时,可能会出现的问题
问题背景
在实现多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"/>