我试图在Spring Boot中使用JWT实现身份验证。在登录功能中,我在SecurityContextHolder中设置身份验证,以便能够在请求时获得它。登录功能可以工作,但当我尝试获取当前登录的用户时,我会变得不受欢迎。我进行了调试,SecurityContextHolder提供了匿名用户。为什么会这样?
UserController类:
@RestController
@CrossOrigin(origins = "http://localhost:3000")
@RequestMapping("/api")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private CustomAuthenticationManager authenticationManager;
@Autowired
private JwtEncoder jwtEncoder;
@PostMapping("/user/login")
public ResponseEntity<User> login(@RequestBody @Valid AuthDto request) {
try {
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));
String userEmail = (String) authentication.getPrincipal();
User user = userService.findUserByEmail(userEmail);
Instant now = Instant.now();
long expiry = 36000L;
String scope = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(joining(" "));
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("uni.pu")
.issuedAt(now)
.expiresAt(now.plusSeconds(expiry))
.subject(format("%s,%s", user.getId(), user.getEmail()))
.claim("roles", scope)
.build();
String token = this.jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
SecurityContextHolder.getContext().setAuthentication(authentication);
return ResponseEntity.ok()
.header(HttpHeaders.AUTHORIZATION, token)
.body(user);
} catch (BadCredentialsException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
@GetMapping("/user/current")
public ResponseEntity<User> getLoggedUser(){
try{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return ResponseEntity.ok()
.body((User)auth.getPrincipal());
}
catch(Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
}
}
WebSecurityConfig:
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
public class WebSecurityConfig {
private static final String[] WHITE_LIST_URLS = {"/api/user/login", "/api/user/current"};
@Autowired
private MyUserDetailsService userDetailsService;
@Value("${jwt.public.key}")
private RSAPublicKey rsaPublicKey;
@Value("${jwt.private.key}")
private RSAPrivateKey rsaPrivateKey;
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(10);
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Enable CORS and disable CSRF
http = http.cors().and().csrf().disable();
// Set session management to stateless
http = http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and();
// Set unauthorized requests exception handler
http = http.exceptionHandling(
(exceptions) -> exceptions.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler()));
http = http.authenticationProvider(authenticationProvider());
// Set permissions on endpoints
http.authorizeHttpRequests().antMatchers(WHITE_LIST_URLS).permitAll().antMatchers("/api/**").authenticated()
// Our private endpoints
.anyRequest().authenticated()
// Set up oauth2 resource server
.and().httpBasic(Customizer.withDefaults()).oauth2ResourceServer().jwt();
return http.build();
}
@Bean
public JwtEncoder jwtEncoder() {
JWK jwk = new RSAKey.Builder(this.rsaPublicKey).privateKey(this.rsaPrivateKey).build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
// Used by JwtAuthenticationProvider to decode and validate JWT tokens
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(this.rsaPublicKey).build();
}
// Extract authorities from the roles claim
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "ROLE_ADMIN > ROLE_INSPECTOR \n ROLE_INSPECTOR > ROLE_STUDENT";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
}
I Spring留档,在请求之间存储SecurityContext部分说:
根据应用程序的类型,可能需要有一种策略来存储用户操作之间的安全上下文。在典型的Web应用程序中,用户登录一次,随后由他们的会话ID标识。服务器缓存会话持续时间的主体信息。在Spring Security中,在请求之间存储SecurityContext的责任落在SecurityContextPeristenceFilter身上,默认情况下,它将上下文存储为HTTP请求之间的HttpSession属性。它为每个请求将上下文恢复到SecurityContextHolder,并且至关重要的是,在请求完成时清除SecurityContextHolder
因此,基本上,当您手动创建安全上下文时,不会创建会话对象。只有当请求完成处理时,Spring Security机制才意识到会话对象为null(当它在处理请求后尝试将安全上下文存储到会话中时)。
在请求结束时,Spring Security会创建一个新的会话对象和会话ID。但是,这个新的会话ID永远不会发送到浏览器,因为它发生在请求结束时,在对浏览器做出响应之后。当下一个请求包含上一个会话ID时,这会导致新会话ID(以及包含手动登录用户的安全上下文)丢失。
我找到了两种解决方案来处理这种情况:
1、第一种解决方案:在会话中保存SecurityContext对象,然后在需要时从会话中提取:
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
然后,从会话中提取它。
private void doAutoLogin(String username, String password, HttpServletRequest request) {
try {
// Must be called from request filtered by Spring Security, otherwise SecurityContextHolder is not updated
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.authenticationProvider.authenticate(token);
logger.debug("Logging in with [{}]", authentication.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
logger.error("Failure in autoLogin", e);
}
};
以下是获取authenticationProvider的方法:
@Configuration public class WebConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationManager authenticationProvider() throws Exception{
return super.authenticationManagerBean();
}
}
似乎Springboot自动配置自己来使用Tomcat的Logback。我想禁用它,并使用我在类路径中提供的一个。 LoggerFactory不是LoggerContext,但Logback位于类路径上。删除Logback或类[org.slf4j.impl.SimpleLoggerFactory]的竞争实现(类org.slf4j.impl.SimpleLoggerFactory)对象必须是类ch.
我尝试将springboot应用程序配置为每天登录一个文件,因此我将logback.xml配置为: 所以当我尝试运行我的应用程序时,我得到了这个错误:
问题内容: 我无法使用JavaFX WebView登录到Google。当我单击“下一步”按钮时,该页面无法加载。 在不同网站上的其他登录也可以正常工作。 这是您可以运行的示例: 屏幕截图在这里 问题答案: 精简版: 在加载页面之前,将以下行添加到您的main方法中: 长版: 我的第一个直觉是JavaScript无法正常工作,但是我测试了伪邮件并正确地得到了错误: 找不到您的Google帐户 因此,
我无法在JavaFX WebView中登录谷歌。当我单击“下一步”按钮时,页面未加载。 不同网站上的其他登录也可以。 这是一个您可以运行的示例: 此处截图
我一直在关注symfony网站上的教程(http://symfony.com/doc/current/security.html)为我的Symfony 3.1项目添加登录名。它可以与HTTP基本身份验证一起工作,但是当我使用这里的教程添加form_登录时(http://symfony.com/doc/current/security/form_login_setup.html)它会一直重定向到登录
以下是错误消息 java.lang.IllegalStateException:无法加载ApplicationContext 一个使用elasticSearch、mysql、redis等的Spingboot项目,谷歌有很多,但他只是一个新的Spingboot。网上的东西不管用。我不知道怎么改。 application-local.yml 应与ES的配置相关 控制器