Spring开发中的异常处理

1.经常会出现@Autowired注入失败,说什么依赖找不到,原因之一是注入依赖对象存在错误,另外一个就是依赖对象确实是不存在

例如看到官网直接注入RestTemplate,如下

@Autowired

 private RestTemplate restTemplate;

按照这个写法,我嫩是注入不成功,http://stackoverflow.com/questions/36151421/could-not-autowire-fieldresttemplate-in-spring-boot-application

解决方法一种是添加下面的代码,我在application中加的

     @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        // 下面是为了解决中文乱码问题
        restTemplate.getMessageConverters()
                .add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        return restTemplate;
    }

2.spring boot使用redis存储spring session

.maximumSessions(1).maxSessionsPreventsLogin(false).invalidSessionStrategy(new SimpleInvalidSessionStrategy())

在一个浏览器登录成功以后,在另一个浏览器登录会把第一个浏览器登录的session覆盖掉,在第一个浏览器接着点击链接地址却发现并没有进入SimpleInvalidSessionStrategy,仍然校验通过,问题目前还没有解决。

网上的说法是当使用SessionManagementConfigurer的maximumSession(int)时不用忘记为应用配置HttpSessionEventPublisher,这样能保证过期的session能够被清除。发现session确实被清理掉了,但是查看ConcurrentSessionFilter类的源代码doFilter函数在没有session的时候并没有做限制判断,浏览器再次请求的时候有session==null和info==null两种情况

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;
    HttpSession session = request.getSession(false);
    if(session != null) {
        SessionInformation info = this.sessionRegistry.getSessionInformation(session.getId());
        if(info != null) {
            if(info.isExpired()) {
                this.doLogout(request, response);
                String targetUrl = this.determineExpiredUrl(request, info);
                if(targetUrl != null) {
                    this.redirectStrategy.sendRedirect(request, response, targetUrl);
                    return;
                }

                response.getWriter().print("This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).");
                response.flushBuffer();
                return;
            }

            this.sessionRegistry.refreshLastRequest(info.getSessionId());
        }
    }

    chain.doFilter(request, response);
}

暂时找不到配置文件哪里有问题,两种临时解决方案

1).拦截器拦截所有的web请求检测是否有登录信息,没有则拒绝

2).在AbstractSecurityInterceptor的doFilter中过滤

在自定义AbstractSecurityInterceptor类的doFilter判断在下面三种情况下拒绝请求

a.session不存在; b:session存在sessionInformation不存在;c:访问路径需要session校验,如获取校验码不需要session校验,/error系统自带的异常处理路径不需要拒绝

自定义SessionRegistryImpl,在新建session时根据用户名把同用户名的session删除这样被挤掉的用户就不需要二次请求才提示session过期,这种方案暂时发现了问题

下面是方案一的实现代码

HttpSessionInterceptora.java

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;

@Slf4j
@Component
public class HttpSessionInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String method = request.getMethod();
        boolean flag = false;
        if(!StringUtils.equalsIgnoreCase(method,"GET")&&StringUtils.equalsIgnoreCase(method,"POST")
                &&StringUtils.equalsIgnoreCase(method,"PUT")
                &&StringUtils.equalsIgnoreCase(method,"DELETE")){
            return true;
        }
        flag = checkHttpSession(response, request);
        return flag;
    }

    private boolean checkHttpSession(HttpServletResponse response, HttpServletRequest request) throws IOException, HCBusinessException {
        HttpSession session = request.getSession(false);
        if (session==null||session.getAttribute("SPRING_SECURITY_CONTEXT") == null) {
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            PrintWriter out;
            out = response.getWriter();
            out.append("{\"success\":false,\"value\":null,\"message\":\"unauthorized\"}");
            out.close();
        }
        return Boolean.TRUE;
    }
}
WebAppConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebAppConfig extends WebMvcConfigurerAdapter {
    @Bean
    public HttpSessionInterceptor httpSessionInterceptor(){
        return new HttpSessionInterceptor();
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(httpSessionInterceptor()).excludePathPatterns("/login","/logout","/getcode","/checkCode");
        super.addInterceptors(registry);
    }
}


下面列出方案二的实现代码

MyFilterSecurityInterceptor.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.FilterInvocation;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Slf4j
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor
	implements Filter {
	private SecurityMetadataSource securityMetadataSource;
	@Autowired
	private SessionRegistry sessionRegistryBean;
	
	public void setSecurityMetadataSource(
			SecurityMetadataSource securityMetadataSource) {
		this.securityMetadataSource = securityMetadataSource;
	}

	@Override
	public Class<? extends Object> getSecureObjectClass() {
		//beforeInvocation需要判断它是否属于FilterInvocation类型
		return FilterInvocation.class;
	}

	@Override
	public SecurityMetadataSource obtainSecurityMetadataSource() {
		return this.securityMetadataSource;
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
		log.debug("------------MyFilterSecurityInterceptor.doFilter()-----------开始了....");
		FilterInvocation fi = new FilterInvocation(request, response, chain);
		invoke(fi);
	}

	public void invoke(FilterInvocation fi) throws IOException, ServletException {
		InterceptorStatusToken token = super.beforeInvocation(fi);
		try{
			String requestUrl=fi.getRequestUrl();
			if((fi.getRequest().getSession(false)!=null&&sessionRegistryBean.getSessionInformation(fi.getRequest().getSession(false).getId())!=null)
			||requestUrl.startsWith("/getcode")
			||requestUrl.startsWith("/checkCode")
			||requestUrl.startsWith("/error")){
				fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
			}else{
				HttpServletResponse httpServletResponse=fi.getResponse();
				httpServletResponse.setCharacterEncoding("UTF-8");
				httpServletResponse.setContentType("application/json; charset=utf-8");
				httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
				PrintWriter out;
				out = httpServletResponse.getWriter();
				out.append("{\"success\":false,\"value\":null,\"message\":\"unauthorized\"}");
				out.close();
			}
		}finally{
			super.afterInvocation(token, null);
		}
		log.debug("-----------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....");
	
	}

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	@Override
	public void destroy() {
	
	}

}
MySessionRegistryImpl.java

import org.MyUser;//自己的UserDetail信息存储类
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetails;

public class MySessionRegistryImpl extends SessionRegistryImpl {

    public MySessionRegistryImpl() {
    }

    public void expireUserSessions(UserDetails newPrincipal) {
        if (newPrincipal instanceof MyUser) {
            int userKeyId = ((MyUser) newPrincipal).getUserKeyId();
            getAllPrincipals().stream()
                    .filter(principal ->
                            (principal instanceof MyUser) && (userKeyId == ((MyUser) principal).getUserKeyId()))
                    .forEach(principal ->
                            getAllSessions(principal, true).stream()
                                    .forEach(information ->//information.expireNow())
                                            removeSessionInformation(information.getSessionId())
                    );
        }
    }

    @Override
    public void registerNewSession(String sessionId, Object principal) {
        expireUserSessions((UserDetails) principal);
        super.registerNewSession(sessionId, principal);
    }
}
使用代码片段

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.HttpSessionEventPublisher;

public abstract class AbstractSecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    @Autowired
    private SessionRegistry sessionRegistryBean;
   
	@Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        //session管理
        http.sessionManagement().sessionFixation().newSession().maximumSessions(1).maxSessionsPreventsLogin(false).expiredUrl("/login").sessionRegistry(sessionRegistryBean)
        .and().invalidSessionStrategy(new SimpleInvalidSessionStrategy());
    }

    //显式声明SessionRegistry,解决maxSessionPreventsLogin无效的问题
    @Bean
    public SessionRegistry sessionRegistryBean(){
        return new MySessionRegistryImpl();
    }
    ...
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值