我正在尝试即将推出的Spring Boot 2.7.0-SNAPSHOT,它使用Spring Security 5.7.0,它不支持WebSecurity配置适配器。
我阅读了这篇博文,但我不确定如何将AuthentiationManager
的默认实现公开给我的JWT授权过滤器。
旧的WebSecurityConfig使用WebSecurityConfig适配器(工作正常):
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Bean
protected AuthenticationManager getAuthenticationManager() throws Exception {
return authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// disable CSRF as we do not serve browser clients
.csrf().disable()
// allow access restriction using request matcher
.authorizeRequests()
// authenticate requests to GraphQL endpoint
.antMatchers("/graphql").authenticated()
// allow all other requests
.anyRequest().permitAll().and()
// JWT authorization filter
.addFilter(new JWTAuthorizationFilter(getAuthenticationManager(), jwtTokenUtils))
// make sure we use stateless session, session will not be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
新的Web安全配置:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http
// disable CSRF as we do not serve browser clients
.csrf().disable()
// allow access restriction using request matcher
.authorizeRequests()
// authenticate requests to GraphQL endpoint
.antMatchers("/graphql").authenticated()
// allow all other requests
.anyRequest().permitAll().and()
// JWT authorization filter
.addFilter(new JWTAuthorizationFilter(authenticationManager, jwtTokenUtils))
// make sure we use stateless session, session will not be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
正如你所见,我再也没有公开的bean了。我无法从Web安全配置适配器获取它。所以我试图直接从filterChain方法中的HttpSecurity获取它,这样我就可以直接将它传递到我的JWT过滤器。
但是我仍然需要一个AuthentiationManager
bean来暴露给我的JWTAuthorizationFilter
:
com中构造函数的参数0。实例配置。安全JWTAuthorizationFilter需要“org”类型的bean。springframework。安全身份验证。找不到AuthenticationManager“”。
我怎么能曝光呢?
这是JWT授权过滤器(检查令牌并验证用户,我有一个自定义的UserDetailsService,它在数据库中检查凭据):
@Component
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private final JWTTokenUtils jwtTokenUtils;
public JWTAuthorizationFilter(AuthenticationManager authManager, JWTTokenUtils jwtTokenUtils) {
super(authManager);
this.jwtTokenUtils = jwtTokenUtils;
}
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
// retrieve request authorization header
final String authorizationHeader = req.getHeader("Authorization");
// authorization header must be set and start with Bearer
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
// decode JWT token
final JWTTokenPayload jwtTokenPayload = jwtTokenUtils.decodeToken(authorizationHeader);
// if user e-mail has been retrieved correctly from the token and if user is not already authenticated
if (jwtTokenPayload.getEmail() != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// authenticate user
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(jwtTokenPayload.getEmail(), null, Collections.singletonList(jwtTokenPayload.getRole()));
// set authentication in security context holder
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
log.error("Valid token contains no user info");
}
}
// no token specified
else {
res.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
// pass request down the chain, except for OPTIONS requests
if (!"OPTIONS".equalsIgnoreCase(req.getMethod())) {
chain.doFilter(req, res);
}
}
}
编辑:
我意识到我可以使用本期中提供的方法在我的JWT过滤器中获取authenticationManager,但我仍然需要一个全局公开的authenticationManager,因为我的控制器中也需要它。
以下是需要注入authenticationManager的身份验证控制器:
java prettyprint-override">@RestController
@CrossOrigin
@Component
public class AuthController {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Autowired
private AuthenticationManager authenticationManager;
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> authenticate(@RequestBody JWTRequest userRequest) {
// try to authenticate user using specified credentials
final Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userRequest.getEmail(), userRequest.getPassword()));
// if authentication succeeded and is not anonymous
if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated()) {
// set authentication in security context holder
SecurityContextHolder.getContext().setAuthentication(authentication);
// get authorities, we should have only one role per member so simply get the first one
final GrantedAuthority grantedAuthority = authentication.getAuthorities().iterator().next();
// generate new JWT token
final String jwtToken = jwtTokenUtils.generateToken(authentication.getPrincipal(), grantedAuthority);
// return response containing the JWT token
return ResponseEntity.ok(new JWTResponse(jwtToken));
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
我也有同样的问题,并通过将AuthenticationManagerBuilder注入过滤器来解决。然后,我让过滤器实现SmartInitializingSingleton,并在生成器上调用getObject()
,以获得AfterSingleton实例化()方法中的AuthenticationManager。
更多关于我是如何想到这个的背景:https://blog.trifork.com/2022/02/25/getting-out-of-a-codependent-relationship-or-how-i-moved-to-a-healthy-component-based-spring-security-configuration/
如果您希望身份验证管理器bean位于Spring上下文中,您可以使用以下解决方案。
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
这种方法解决了我的问题,您可以在任何需要的地方注入AuthenticationManager。
要能够获取并将AuthenticationManager(您无法再从弃用的Web安全配置适配器中获取)传递到过滤器,一个解决方案是拥有一个专门的配置器,该配置器将负责添加过滤器。(这是从这里提供的解决方案中获得的灵感。编辑:现已在文档中正式发布)。
创建自定义HTTP配置器:
@Component
public class JWTHttpConfigurer extends AbstractHttpConfigurer<JWTHttpConfigurer, HttpSecurity> {
private final JWTTokenUtils jwtTokenUtils;
public JWTHttpConfigurer(JWTTokenUtils jwtTokenUtils) {
this.jwtTokenUtils = jwtTokenUtils;
}
@Override
public void configure(HttpSecurity http) {
final AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.antMatcher("/graphql").addFilter(new JWTAuthorizationFilter(authenticationManager, jwtTokenUtils));
}
}
然后只需在安全配置中应用它:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// disable CSRF as we do not serve browser clients
.csrf().disable()
// allow access restriction using request matcher
.authorizeRequests()
// authenticate requests to GraphQL endpoint
.antMatchers("/graphql").authenticated()
// allow all other requests
.anyRequest().permitAll().and()
// JWT authorization filter
.apply(new JWTHttpConfigurer(jwtTokenUtils)).and()
// make sure we use stateless session, session will not be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
在某些情况下,您需要全局公开身份验证管理器,以便它在应用程序中的任何位置都可用。
在Spring上下文中使用AuthenticationManager bean的一个解决方案是从导出身份验证配置的AuthenticationConfiguration获取它(归功于Andrei Daneliuc下面的回答):
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
然后,如果需要在过滤器链中检索它,可以使用authenticationManager(http.getSharedObject(AuthenticationConfiguration.class))。
因此,整个安全配置将是:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// disable CSRF as we do not serve browser clients
.csrf().disable()
// match GraphQL endpoint
.antMatcher("/graphql")
// add JWT authorization filter
.addFilter(new JWTAuthorizationFilter(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class)), jwtTokenUtils))
// allow access restriction using request matcher
.authorizeRequests()
// authenticate requests to GraphQL endpoint
.antMatchers("/graphql").authenticated()
// allow all other requests
.anyRequest().permitAll().and()
// make sure we use stateless session, session will not be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
全局公开身份验证管理器的另一种解决方案是使用自定义AuthenticationManager
,作为整个应用程序可用的bean,在我们的例子中,它的作用与默认的DaoAuthenticationProvider
实现完全相同(即使用自定义UserDetailsService
从数据库中获取用户详细信息,使用配置的PasswordEncoder
验证密码,然后返回UsernamePasswordAuthenticationToken
以呈现身份验证
):
@Component
public class CustomAuthenticationManager implements AuthenticationManager {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final UserDetails userDetail = customUserDetailsService.loadUserByUsername(authentication.getName());
if (!passwordEncoder().matches(authentication.getCredentials().toString(), userDetail.getPassword())) {
throw new BadCredentialsException("Wrong password");
}
return new UsernamePasswordAuthenticationToken(userDetail.getUsername(), userDetail.getPassword(), userDetail.getAuthorities());
}
}
这样,您可以在添加过滤器时在安全配置中使用它:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// disable CSRF as we do not serve browser clients
.csrf().disable()
// match GraphQL endpoint
.antMatcher("/graphql")
// add JWT authorization filter
.addFilter(new JWTAuthorizationFilter(new CustomAuthenticationManager(), jwtTokenUtils))
// allow access restriction using request matcher
.authorizeRequests()
// authenticate requests to GraphQL endpoint
.antMatchers("/graphql").authenticated()
// allow all other requests
.anyRequest().permitAll().and()
// make sure we use stateless session, session will not be used to store user's state
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
它可以注入应用程序中的任何其他位置,即控制器中:
@RestController
@CrossOrigin
@Component
public class AuthController {
@Autowired
private JWTTokenUtils jwtTokenUtils;
@Autowired
private CustomAuthenticationManager authenticationManager;
@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> authenticate(@RequestBody JWTRequest userRequest) {
// try to authenticate user using specified credentials
final Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userRequest.getEmail(), userRequest.getPassword()));
// if authentication succeeded and is not anonymous
if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated()) {
// set authentication in security context holder
SecurityContextHolder.getContext().setAuthentication(authentication);
// get authorities, we should have only one role per member so simply get the first one
final GrantedAuthority grantedAuthority = authentication.getAuthorities().iterator().next();
// generate new JWT token
final String jwtToken = jwtTokenUtils.generateToken(authentication.getPrincipal(), grantedAuthority);
// return response containing the JWT token
return ResponseEntity.ok(new JWTResponse(jwtToken));
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
请注意,您可能还希望使用自定义AuthentiationEntryPoint
,在引发BadCreentialsException
时返回401而不是500。
我正在尝试更新到Spring Boot 2.7.0-SNAPSHOT。此版本中不推荐使用WebSecurity配置适配器。 旧的WebSecurityConfig with WebSecurityConfigurerAdapter(工作正常): 读完这篇博文后,我修改了新的WebSecurityConfig: 我能够修复两种方法。(#configure(WebSecurity web)和#conf
我已经创建了一个 除了一些其他的: 在我的aplication.properties 而这是的入口点: 仍然,我没有看到下的endpointhttps://localhost:8443/v3/api-docs.只有那些来自的endpoint: 我使用的是Spring Boot。 这是我使用的整个pom.xml: 我已经尝试按照这里的建议添加依赖项 但它仍然不起作用。 知道这是什么原因吗?
假设我想同时运行多个Docker容器。 有没有什么公式我可以用来提前找出单个Docker主机可以同时运行多少个容器?例如,容器本身需要考虑多少CPU、内存和CO?
我已经配置了普罗米修斯来获取一些来源的指标。 cadvisor 问题是。 在Prometheus UI中,我可以看到所有的目标,在图形中,我可以查询所有与nodejs相关的指标。但是如果我做,我只看到与普罗米修斯和节点出口商相关的指标。没有任何与nodejs应用程序或hazelcast相关的内容。 直接请求nodejs应用程序毫无问题地返回所有这些值。 什么会导致这样的问题?
如何使MDC信息显示在Azure Insights中。目前我只在跟踪日志中看到它。 null application.yaml 谢谢你的帮助
问题内容: 我正在开发一个简单的Webapp,它将域模型公开为RESTful资源。我打算将JPA2(Hibernate)与SpringMVC REST支持一起使用。 在将Hibernate实体编组为XML / JSON时,如果该实体分离,它将为惰性子项关联抛出LazyLoadingException。如果实体仍然连接到Hibernate Session,它将几乎加载整个数据库。 我尝试使用推土机C