工作中用到了验证用户是否登陆,判断是否有session值,想到了用SpringMVC HandlerInterceptorAdapter拦截器,方便,实用性大。
Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。
Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。它有三个方法:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)
在preHandle中,可以进行编码、安全控制等处理;
在postHandle中,有机会修改ModelAndView;
在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
如果基于xml配置使用Spring MVC,
可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping进行Url映射(相当于struts的path映射)和拦截请求(注入interceptors),
如果基于注解使用Spring MVC,可以使用DefaultAnnotationHandlerMapping注入interceptors。
注意无论基于xml还是基于注解,HandlerMapping bean都是需要在xml中配置的。
我做的功能是用户登录验证,代码如下:
@Controller
public class SessionInjectionInterceptor extends HandlerInterceptorAdapter {
private static Logger log = Logger
.getLogger(SessionInjectionInterceptor.class);
@Autowired
private HttpSession session;
@Autowired
private ILoginService loginService;
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// 获取当前登录人信息
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
// 判断当前用户session为null,并且已经做了登陆验证。
if (authentication != null
&& session
.getAttribute(Constants.Session.ACCOUNT_ATTRIBUTE_NAME) == null) {
String username = authentication.getName();
if (!"anonymousUser".equals(username)) {
// account
Account account = (Account) loginService
.getAccountByName(username);
session.setAttribute(Constants.Session.ACCOUNT_ATTRIBUTE_NAME,
account);
} else {
// 做无浏览权限验证,或者未登陆用户,转发到登陆页面
session.setAttribute(Constants.Session.ACCOUNT_ATTRIBUTE_NAME,
null);
request.getRequestDispatcher("/login.jsp").forward(
request, response);
return false;
}
}
return super.preHandle(request, response, handler);
}
}
用户登录验证,和前台页面配合:
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static Logger log = Logger
.getLogger(CustomAuthenticationProvider.class);
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// 获取前台传送的用户名和密码
String username = authentication.getName();
String password = authentication.getCredentials().toString();
log.debug("username = " + username + " password = " + password);
// 根据用户名密码查询用户是否存在,并返回编码值
UserResult ur = authorize(username, password);
log.debug(ur.toJson());
// 判断编码值是否是success,
if (ur.getCode().equals(UserResult.Code.SUCCESS)) {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(username, password,
grantedAuths);
} else {
throw new AuthenticationException(ur.getMsg().getContent()) {
private static final long serialVersionUID = -5506038021601762240L;
};
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private UserResult authorize(final String name, final String password) {
log.info("authorize begin. name = " + name);
if (name == "" || null == name) {
return new UserResult(Code.FAIL, Msg.ILLEGAL_USERNAME);
}
// TODO 根据名称查询用户是否存在,暂用自己测试
String sql = "select * from ACCOUNT where LLOGIN_ID = ? and PPASSWORD = ?";
Object[] params = { name,password };
Account account = null;
try {
account = jdbcTemplate.queryForObject(sql, params, new BeanPropertyRowMapper<Account>(Account.class));
// 判断根据用户名查找用户是否存在
if (null == account) {
log.info("username 是不存在的名字 = " + name);
return new UserResult(Code.FAIL, Msg.USER_NOT_EXIST);
}
} catch (Exception e) {
log.error(e, e);
if (e instanceof IncorrectResultSizeDataAccessException) {
// 没有结果,或者结果数据不符合
account = null;
log.info("username 是不存在的名字 = " + name);
return new UserResult(Code.FAIL, Msg.USER_NOT_EXIST);
} else {
return new UserResult(Code.FAIL, Msg.DATA_ACCESS_EXCEPTION);
}
}
log.debug("authorize end.");
return new UserResult(Code.SUCCESS, Msg.SUCCESS);
}
}
XML配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<beans:bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<beans:property name="location" value="classpath:c3p0.properties"/>
</beans:bean>
<beans:bean id="securityDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<beans:property name="driverClass" value="com.mysql.jdbc.Driver" />
<beans:property name="jdbcUrl" value="${jdbc.url}" />
<beans:property name="user" value="${jdbc.username}" />
<beans:property name="password" value="${jdbc.password}" />
</beans:bean>
<!-- 用户登录验证 -->
<beans:bean class="com.impl.CustomAuthenticationProvider" id="customAuthenticationProvider">
<beans:property name="dataSource" ref="securityDataSource" />
</beans:bean>
<!-- 配置静态页面 -->
<http pattern="/fonts/**" security="none"/>
<http pattern="/js/**" security="none"/>
<http pattern="/css/**" security="none"/>
<http pattern="/images/**" security="none"/>
<!-- 配置不拦截的请求 -->
<http auto-config="true" use-expressions="true" create-session="ifRequired">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/proxy/*" access="permitAll" />
<intercept-url pattern="/register" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<form-login login-page="/login" default-target-url="/homePage" always-use-default-target="true" />
<logout invalidate-session="true" logout-success-url="/homePage" logout-url="/j_spring_security_logout"/>
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
</beans:beans>
除了以上过滤的请求,其余全部会拦截,根据自己需求,进行更改。
可以根据这个配置多个拦截器,赋予它不同的分工。