工作中用到了验证用户是否登陆,判断是否有session值,想到了用SpringMVC HandlerInterceptorAdapter拦截器,方便,实用性大。
Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。
Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。它有三个方法:
1 2 3 4 5 6 7 8 9 10 11 12 |
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中配置的。
我做的功能是用户登录验证,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
@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); } } |
用户登录验证,和前台页面配合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
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配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?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> |
除了以上过滤的请求,其余全部会拦截,根据自己需求,进行更改。
可以根据这个配置多个拦截器,赋予它不同的分工。