前景提要。目前系统是在通过/ 根目录时判断用户权限,并根据jwt来返回对应的权限。因集成seata时发现2021版本不支持jdk17。所以直接升级3.2.5
问题描述
通过/认证之后。对中间问写入权限。然后浏览器在后续请求返回403.后台通过日志判断,发现每次请求时都没有认证信息。有个简单点的解决方法,直接去掉/判断或者在每次无权限时都加入信息。pass(后续可能会在里面访问数据库,可能会导致系统缓慢)。通过查看每次的请求,发现sessin会发生变化,在浏览器未重新启动和清理缓存的时候。但同一个session也会有这个问题
WebSecurityConfig文件对比
升级前
import org.springframework.context.annotation.Configuration;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/js/**", "/lib/**", "/css/**", "/data/**", "/image/**", "/pages/**").permitAll()
.and().csrf().disable().headers().frameOptions().sameOrigin().and()
.httpBasic()
.disable();
}
}
升级后
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) // This replaces @EnableGlobalMethodSecurity
public class WebSecurityConfig {
@Autowired
private JWTAuth jwtAuth;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/js/**", "/lib/**", "/css/**", "/data/**", "/images/**", "/pages/**").permitAll()
.anyRequest().authenticated()
)
.csrf(csrf -> csrf.disable()) //关闭csrf
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)) //解决问题的关键
.headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin())) //可以在iframe中访问
.addFilterBefore(jwtAuth, BasicAuthenticationFilter.class)//加入拦截器
.httpBasic(httpBasic -> httpBasic.disable()); //关闭http登录框
return http.build();
}
}
拦截器代码 简易版。因为网站比较简单,所以只有用户id权限标识不一样。仅作为示例来对比,请勿直接使用
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.lj2508.sso.util.JwtUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* <p>Title: JWTAuth</p>
* <p>Description: </p>
* @author [email protected]
* @date 2020年5月18日
*/
@Component
public class JWTAuth extends OncePerRequestFilter {
@Autowired
private RedisTemplate<String, String> redis;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if ( request.getRequestURI().equals("/")) {
//授权和验证
Set<GrantedAuthority> authorities = new HashSet<>();
String id = "";
try {
id = JwtUtil.getJwt(request).getId();
} catch (Exception e) {
//代表用户未登录
response.sendRedirect("登录网站");
return;
}
if (id.equals("1"))
redis.opsForValue().set("1", "ROLE_ADMIN");
String authName = redis.opsForValue().get(id);
if (StringUtils.isEmpty(authName))
authName = "ROLE_USER";
authorities.add(new SimpleGrantedAuthority(authName));
UsernamePasswordAuthenticationToken auPasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(id,null, authorities);
SecurityContextHolder.getContext().setAuthentication(auPasswordAuthenticationToken);
}
doFilter(request, response, filterChain);
}
}
问题解决是修改了
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED))
之后。通过设置之后。程序访问正常。仅在/时验证信息,后续可以直接用上下文中获取权限标识
public enum SessionCreationPolicy {
/**
* Always create an {@link HttpSession}
*/
ALWAYS,
/**
* Spring Security will never create an {@link HttpSession}, but will use the
* {@link HttpSession} if it already exists
*/
NEVER,
/**
* Spring Security will only create an {@link HttpSession} if required
*/
IF_REQUIRED,
/**
* Spring Security will never create an {@link HttpSession} and it will never use it
* to obtain the {@link SecurityContext}
*/
STATELESS
}
评论区