源码下载 我的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;
}
}
}