天天看点

shiro + extjs

源码下载 我的GITHUB

shiro 相比与spring security配置上简单很多。

加载shiro config

<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
           classpath:shiro-config.xml
        </param-value>
    </context-param>
           

添加shrio过滤器,这里需要注意的是,过滤器的名字shiroFilter必须与配置文件里面的bean名字相同

<filter>
    	<filter-name>shiroFilter</filter-name>
    	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    	<init-param>
        	<param-name>targetFilterLifecycle</param-name>
        	<param-value>true</param-value>
    	</init-param>
	</filter>

	<filter-mapping>
    	<filter-name>shiroFilter</filter-name>
    	<url-pattern>/*</url-pattern>
	</filter-mapping>
           

shiroFilter配置,这里需要配置一个 formAuthenticationFilter,作用在后面会提到,主要是因为extjs的ajax请求。

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager"/>
		<!-- override these for application-specific URLs if you like:-->
		<property name="loginUrl" value="/login.html"/>
    	<property name="successUrl" value="/index.html"/>
    	<property name="unauthorizedUrl" value="/unauthorized.html"/>
    	<property name="filters">
        	<util:map>
            	<entry key="authc" value-ref="formAuthenticationFilter"/>
        	</util:map>
    	</property>
    	<property name="filterChainDefinitions">
    		<value>
    			/comp/** = anon
    			/pages/** = anon
    			/resources/** = anon
    			/view/** = anon
    			/** = authc
    		</value>
    	</property>
	</bean>
           

shiroFilter中引用了 securityManageer。如果参考shiro官网的同学请注意,官网documentation中的manager是org.apache.shiro.mgt.DefaultWebSecurityManager。在1.2.2中,这个类是没有实现WebSecurityManager的,所以会跑出unimplement WebSecurityManager的异常。

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    	<!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. 如果有多个realm,需要用reamls参数,为实践。-->
    	<property name="realm" ref="fastRealm"/>
    </bean>
           

引用的realm是可以重写的。

<bean id="fastRealm" class="com.chinadreamer.contacts.filter.shiro.UserRealm"></bean>
           

重写realm。

重写的doGetAuthorizationInfo与doGetAuthenticationInfo在官网中已经很明确的说明了,这里不再解释。

我说下这里会遇到的一个问题。shiro跟spring的filter并没有优先级之分,而用户验证,一般都会涉及到datasource bean的引用,比方这里的userservice。所以需要注意的是必须在spring bean加载之后,进入shiro,否则是无法加载的。

我的做法是在applicationContext.xml 结尾才引入shiro-config.xml,这样就可以获取到bean了。

引入方式比较常规

<import resource="shiro-config.xml"/>
           
public class UserRealm extends AuthorizingRealm{
	private final static Logger LOGGER = Logger.getLogger(UserRealm.class);
	
	
	@Autowired
	private UserService userService;
	
	public UserRealm(){
		super();
		LOGGER.info(" init user realm ");
		setAuthenticationTokenClass(UsernamePasswordToken.class);
	}
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		User user = ShiroUtils.getUser();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        return authorizationInfo;
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());
        User user;
        try {
			user = this.userService.userLogin(username, password);
		} catch (Exception e) {
			throw new AuthenticationException(e.getMessage(),e);
		}
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password.toCharArray(), getName());
		return info;
	}

}
           
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
           

简单的shiro到这就可以了。但是如果配合extjs,就需要控制ajax返回,大家知道extj是根据response会写的success 字段来判断的。

<!--替换默认的form 验证过滤器-->
    <bean id="formAuthenticationFilter" class="com.chinadreamer.contacts.filter.shiro.FastFormAuthenticationFilter">
    </bean>
           

提供一份源码,根据各自的需求改动,这是其他大大提供的。

package com.chinadreamer.contacts.filter.shiro;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

public class FastFormAuthenticationFilter extends FormAuthenticationFilter{
	private static final Logger LOGGER =  Logger.getLogger(FastFormAuthenticationFilter.class);
	
	/* 
     *  主要是针对登入成功的处理方法。对于请求头是AJAX的之间返回JSON字符串。 
     */  
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token,
			Subject subject, ServletRequest request, ServletResponse response)
			throws Exception {
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		HttpServletResponse httpServletResponse = (HttpServletResponse) response;

		if (!"XMLHttpRequest".equalsIgnoreCase(httpServletRequest
				.getHeader("X-Requested-With"))) {// 不是ajax请求
			issueSuccessRedirect(request, response);
		} else {
			httpServletResponse.setCharacterEncoding("UTF-8");
			PrintWriter out = httpServletResponse.getWriter();
			out.println("{success:true,message:'登入成功'}");
			out.flush();
			out.close();
		}
		return false;
	}
	
	/** 
     * 主要是处理登入失败的方法 
     */  
    @Override  
    protected boolean onLoginFailure(AuthenticationToken token,  
            AuthenticationException e, ServletRequest request,  
            ServletResponse response) {  
        if (!"XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request)  
                .getHeader("X-Requested-With"))) {// 不是ajax请求  
            setFailureAttribute(request, e);  
            return true;  
        }  
        try {  
            response.setCharacterEncoding("UTF-8");  
            PrintWriter out = response.getWriter();  
            String message = e.getClass().getSimpleName();  
            if ("IncorrectCredentialsException".equals(message)) {  
                out.println("{success:false,message:'密码错误'}");  
            } else if ("UnknownAccountException".equals(message)) {  
                out.println("{success:false,message:'账号不存在'}");  
            } else if ("LockedAccountException".equals(message)) {  
                out.println("{success:false,message:'账号被锁定'}");  
            } else {  
                out.println("{success:false,message:'未知错误'}");  
            }  
            out.flush();  
            out.close();  
        } catch (IOException e1) {  
            // TODO Auto-generated catch block  
            e1.printStackTrace();  
        }  
        return false;  
    }  
    
    /** 
     * 所有请求都会经过的方法。 
     */  
    @Override  
    protected boolean onAccessDenied(ServletRequest request,  
            ServletResponse response) throws Exception {  
  
        if (isLoginRequest(request, response)) {  
            if (isLoginSubmission(request, response)) {  
                if (LOGGER.isTraceEnabled()) {  
                	LOGGER.trace("Login submission detected.  Attempting to execute login.");  
                }  
                if ("XMLHttpRequest"  
                        .equalsIgnoreCase(((HttpServletRequest) request)  
                                .getHeader("X-Requested-With"))) {// 不是ajax请求  
//                    String vcode = request.getParameter("vcode");  
//                    HttpServletRequest httpservletrequest = (HttpServletRequest) request;  
//                    String vvcode = (String) httpservletrequest  
//                            .getSession()  
//                            .getAttribute(  
//                                    com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);  
//                    if (vvcode == null || "".equals(vvcode)  
//                            || !vvcode.equals(vcode)) {  
//                        response.setCharacterEncoding("UTF-8");  
//                        PrintWriter out = response.getWriter();  
//                        out.println("{success:false,message:'验证码错误'}");  
//                        out.flush();  
//                        out.close();  
//                        return false;  
//                    }  
                }  
                return executeLogin(request, response);  
            } else {  
                if (LOGGER.isTraceEnabled()) {  
                	LOGGER.trace("Login page view.");  
                }  
                // allow them to see the login page ;)  
                return true;  
            }  
        } else {  
            if (LOGGER.isTraceEnabled()) {  
            	LOGGER.trace("Attempting to access a path which requires authentication.  Forwarding to the "  
                        + "Authentication url [" + getLoginUrl() + "]");  
            }  
            if (!"XMLHttpRequest"  
                    .equalsIgnoreCase(((HttpServletRequest) request)  
                            .getHeader("X-Requested-With"))) {// 不是ajax请求  
                saveRequestAndRedirectToLogin(request, response);  
            } else {  
//                response.setCharacterEncoding("UTF-8");  
//                PrintWriter out = response.getWriter();  
//                out.println("{success:true,message:'login'}");  
//                out.flush();  
//                out.close();  
                return true;
            }  
            return false;  
        }  
    }  
}
           

继续阅读