java 没有会话的 Spring Boot 基本身份验证(无状态会话)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37820315/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Boot Basic Authentication without Session (Stateless Session)
提问by The Coder
I have configured Basic Authentication my Spring-Boot application. Everything is Java Config, no xml.
我已经为 Spring-Boot 应用程序配置了基本身份验证。一切都是 Java Config,没有 xml。
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Authenticate username -> admin, password -> admin & set role as "ROLE_USER"
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
// All Requests should be Authenticated
.anyRequest().authenticated()
.and()
// Enable Basic Authentication
.httpBasic()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/main", true)
.loginProcessingUrl("/session")
.usernameParameter("Username").passwordParameter("Password")
.and()
.logout().logoutUrl("/logout").permitAll()
.and().csrf().disable();
}
}
It's configured for both Basic authentication and normal form login. When I tested the basic authentication from Rest-Client on Firefox, I can access the secure url "/main". But in the response headers, I'm getting Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D;
.
它针对基本身份验证和普通表单登录进行了配置。当我在 Firefox 上测试来自 Rest-Client 的基本身份验证时,我可以访问安全 URL“/main”。但是在响应标头中,我得到Set-Cookie: JSESSIONID=301225C7AE7C74B0892887389996785D;
.
I don't want cookies to be generated for basic authentication. I want true Stateless session
for Basic Authentication. Do note that I need cookies to be generated for form-login to work, so disabling cookies is not an option. I know about the create-session="stateless"
in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
我不希望为基本身份验证生成 cookie。我想要Stateless session
基本身份验证为真。请注意,我需要为表单登录生成 cookie 才能工作,因此禁用 cookie 不是一个选项。我知道create-session="stateless"
in xml 配置,但是有什么方法可以在 Java 配置中做同样的事情,以便基本身份验证是无状态的,表单身份验证是有状态的..?
回答by shazin
I know about the create-session="stateless" in xml configuration, but is there any way to do the same in Java config so that Basic Authentication is Stateless and Form-Authentication is Statefull..?
You can do the following.
您可以执行以下操作。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
And For your problem following custom Java Config can be used.
对于您的问题,可以使用自定义 Java 配置。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Inject
UserDetailsService userService;
@Bean
public AuthenticationManager authenticationManager() throws Exception {
AuthenticationManager authenticationManager = new ProviderManager(
Arrays.asList(authenticationProvider()));
return authenticationManager;
}
@Bean
public AuthenticationProvider authenticationProvider() throws Exception {
CustomAuthenticationProvider authenticationProvider = new CustomAuthenticationProvider();
authenticationProvider.setUserDetailsService(userService);
authenticationProvider.setSaltSource(saltSource());
authenticationProvider.setPasswordEncoder(passwordEncoder());
authenticationProvider.afterPropertiesSet();
return authenticationProvider;
}
@Bean
public SaltSource saltSource() throws Exception {
ReflectionSaltSource saltSource = new ReflectionSaltSource();
saltSource.setUserPropertyToUse("salt");
saltSource.afterPropertiesSet();
return saltSource;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
@Bean
public FilterChainProxy springSecurityFilterChain()
throws ServletException, Exception {
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>();
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/login**")));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/resources/**")));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/api/**"),
securityContextPersistenceFilterASCFalse(),
basicAuthenticationFilter(), exceptionTranslationFilter(),
filterSecurityInterceptor()));
securityFilterChains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/**"),
securityContextPersistenceFilterASCTrue(), logoutFilter(),
usernamePasswordAuthenticationFilter(),
exceptionTranslationFilter(), filterSecurityInterceptor()));
return new FilterChainProxy(securityFilterChains);
}
@Bean
public SecurityContextPersistenceFilter securityContextPersistenceFilterASCTrue() {
return new SecurityContextPersistenceFilter(
new HttpSessionSecurityContextRepository());
}
@Bean
public SecurityContextPersistenceFilter securityContextPersistenceFilterASCFalse() {
HttpSessionSecurityContextRepository httpSessionSecurityContextRepository = new HttpSessionSecurityContextRepository();
httpSessionSecurityContextRepository.setAllowSessionCreation(false);
return new SecurityContextPersistenceFilter(
httpSessionSecurityContextRepository);
}
@Bean
public ExceptionTranslationFilter exceptionTranslationFilter() {
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
new LoginUrlAuthenticationEntryPoint("/login"));
AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl();
accessDeniedHandlerImpl.setErrorPage("/exception");
exceptionTranslationFilter
.setAccessDeniedHandler(accessDeniedHandlerImpl);
exceptionTranslationFilter.afterPropertiesSet();
return exceptionTranslationFilter;
}
@Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter()
throws Exception {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter
.setAuthenticationManager(authenticationManager());
usernamePasswordAuthenticationFilter.setAllowSessionCreation(true);
SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler(
"/index");
successHandler.setAlwaysUseDefaultTargetUrl(true);
usernamePasswordAuthenticationFilter
.setAuthenticationSuccessHandler(successHandler);
usernamePasswordAuthenticationFilter
.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(
"/login?error=true"));
usernamePasswordAuthenticationFilter
.setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
usernamePasswordAuthenticationFilter.afterPropertiesSet();
return usernamePasswordAuthenticationFilter;
}
@Bean
public FilterSecurityInterceptor filterSecurityInterceptor()
throws Exception {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor
.setAuthenticationManager(authenticationManager());
filterSecurityInterceptor
.setAccessDecisionManager(accessDecisionManager());
filterSecurityInterceptor.setRunAsManager(runAsManager());
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
List<ConfigAttribute> configs = new ArrayList<ConfigAttribute>();
configs.add(new org.springframework.security.access.SecurityConfig(
"isAuthenticated()"));
requestMap.put(new AntPathRequestMatcher("/**"), configs);
FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource = new ExpressionBasedFilterInvocationSecurityMetadataSource(
requestMap, new DefaultWebSecurityExpressionHandler());
filterSecurityInterceptor
.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
filterSecurityInterceptor.afterPropertiesSet();
return filterSecurityInterceptor;
}
public AffirmativeBased accessDecisionManager() throws Exception {
List<AccessDecisionVoter> voters = new ArrayList<AccessDecisionVoter>();
voters.add(new WebExpressionVoter());
voters.add(new RoleVoter());
AffirmativeBased affirmativeBased = new AffirmativeBased(voters);
affirmativeBased.setAllowIfAllAbstainDecisions(false);
affirmativeBased.afterPropertiesSet();
return affirmativeBased;
}
@Bean
public RunAsManager runAsManager() throws Exception {
RunAsManagerImpl runAsManager = new RunAsManagerImpl();
runAsManager.setKey("V_RUN_AS");
runAsManager.afterPropertiesSet();
return runAsManager;
}
@Bean
public LogoutFilter logoutFilter() throws ServletException {
List<LogoutHandler> handlers = new ArrayList<LogoutHandler>();
handlers.add(new CookieClearingLogoutHandler("JSESSIONID"));
handlers.add(new SecurityContextLogoutHandler());
LogoutFilter logoutFilter = new LogoutFilter("/login",
handlers.toArray(new LogoutHandler[] {}));
logoutFilter.afterPropertiesSet();
return logoutFilter;
}
@Bean
public RequestContextFilter requestContextFilter() {
return new RequestContextFilter();
}
@Bean
public BasicAuthenticationFilter basicAuthenticationFilter()
throws Exception {
BasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
basicAuthenticationEntryPoint.setRealmName("V_REALM");
basicAuthenticationEntryPoint.afterPropertiesSet();
BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(
authenticationManager(), basicAuthenticationEntryPoint);
basicAuthenticationFilter
.setAuthenticationDetailsSource(new CustomWebAuthenticationDetailsSource());
basicAuthenticationFilter.afterPropertiesSet();
return basicAuthenticationFilter;
}
}
This configuration creates two different authentication mechanism.
此配置创建了两种不同的身份验证机制。
For any request starting with /api/*
it will be using a basicAuthenticationFilter
and securityContextPersistenceFilterASCFalse
with Session Creation False.
对于任何以/api/*
它开头的请求,都将使用basicAuthenticationFilter
andsecurityContextPersistenceFilterASCFalse
和 Session Creation False。
For any request starting with /*
it will be using a usernamePasswordAuthenticationFilter
and securityContextPersistenceFilterASCTrue
with Session Creation True.
对于任何以/*
它开头的请求,都将使用usernamePasswordAuthenticationFilter
andsecurityContextPersistenceFilterASCTrue
和 Session Creation True。
You can make use of this and alter it to cater your problem.
您可以利用它并对其进行更改以满足您的问题。
回答by MattW
For anyone else that comes across this, here's something else to check.
对于遇到此问题的任何其他人,请检查其他内容。
I was hitting this same problem with Spring Boot and even with
我在使用 Spring Boot 时遇到了同样的问题,甚至使用
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
I was still seeing JSESSIONID cookies being set. In my case (using JWT), the missing piece seemed to be setting setAllowSessionCreation on the HttpSessionSecurityContextRepository
object, like this:
我仍然看到正在设置 JSESSIONID cookie。就我而言(使用 JWT),缺少的部分似乎是在HttpSessionSecurityContextRepository
对象上设置 setAllowSessionCreation ,如下所示:
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final MyTokenAuthService authenticationService;
private SecurityContextRepository repository = new HttpSessionSecurityContextRepository();
protected final Logger logger = LoggerFactory.getLogger(getClass().getName());
public StatelessAuthenticationFilter(MyTokenAuthService authenticationService) {
this.authenticationService = authenticationService;
((HttpSessionSecurityContextRepository) repository).setAllowSessionCreation(false);
}
}
What pointed me this were these lines in HttpSessionSecurityContextRepository
:
向我指出这是以下几行HttpSessionSecurityContextRepository
:
private boolean allowSessionCreation = true;