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();
}
...
}