Web Security Configr Adapter를 사용하지 않고 Authentication Manager를 노출하는 Spring Security
Spring Security 5.7.0을 사용하는 착신 Spring Boot 2.7.0-SNAPSHOT을 시도하고 있습니다.Spring Security 5.7.0은 Spring Security 5.7.0을 사용하지 않습니다.WebSecurityConfigurerAdapter.
이 블로그 투고를 읽었습니다만, 디폴트 실장을 어떻게 공개하는지는 잘 모르겠습니다.AuthenticationManagerJWT 인증 필터에 접속합니다.
옛것WebSecurityConfig,사용.WebSecurityConfigurerAdapter(정상 동작):
@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);
}
}
새로운WebSecurityConfig:
@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();
}
}
보다시피 나는 없다.AuthenticationManager더 이상 노출되지 않은 콩.에서는 얻을 수 없습니다.WebSecurityConfigurerAdapter그래서 제가 직접 받아보려고 했는데HttpSecurity에서filterChainJWT 필터에 직접 전달할 수 있습니다.
하지만 난 여전히 필요한 게이가 필요해AuthenticationManager내 콩에 노출되다JWTAuthorizationFilter:
com.example.config의 컨스트럭터의 파라미터 0.보안.JWTAuthorizationFilter에는 'org.springframework' 유형의 빈이 필요합니다.보안.인증이 필요합니다.AuthenticationManager'를 찾을 수 없습니다.
어떻게 노출시킬 수 있죠?
여기 JWT 인증 필터(토큰 체크 및 사용자 인증, 사용자 지정 있음)가 있습니다.UserDetailsService데이터베이스 내의 credential을 체크합니다).
@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);
}
}
}
편집:
나는 내가 그럭저럭 할 수 있다는 걸 깨달았어authenticationManagerJWT 필터는 이 호에서 제공된 방법을 사용하여 사용하지만, 여전히 필요한 것은AuthenticationManager전 세계에 노출될 수 있습니다. 제 컨트롤러에도 필요하기 때문입니다.
다음 그림에 인증 컨트롤러가 있습니다.authenticationManager주입 대상:
@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();
}
}
현지의AuthenticationManager
이 솔루션을 통해 고객의 요구를 충족시킬 수 있습니다.AuthenticationManager(더 이상 비권장자에게서는 얻을 수 없습니다).WebSecurityConfigurerAdapter)는 필터 추가를 담당하는 전용 컨피규레이터가 있어야 합니다.(이는 여기에 제시된 솔루션에서 영감을 얻은 것입니다.편집 : 및 매뉴얼에 정식으로 기재되어 있습니다).
커스텀 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에 적용합니다.
@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();
}
}
세계적인AuthenticationManager
경우에 따라서는 어플리케이션 내 어디에서나 사용할 수 있도록 인증 매니저를 글로벌하게 공개해야 합니다.
이 솔루션을 통해AuthenticationManager봄의 문맥에서 빈은 그것을 얻는 것이다.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할 수 있는 이인 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「Default」 「D」와 같은 처리를 실시합니다.DaoAuthenticationProvider합니다)UserDetailsService 정보를 된 사용자 정보를 사용하여 합니다.PasswordEncoder a, a를 UsernamePasswordAuthenticationTokenAuthentication
@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에서 사용할 수 있도록 하기 위해 다음과 같이 합니다.
@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();
}
}
, 커스텀을 도 모릅니다.AuthenticationEntryPoint 401을 BadCredentialsException인스톨 됩니다.
AuthenticationManager bean을 스프링콘텍스트에 포함할 경우 다음 솔루션을 사용할 수 있습니다.
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
이 접근방식으로 문제가 해결되었습니다.필요한 장소에서 Authentication Manager를 삽입할 수 있습니다.
문제가 '이러다'를 .AuthenticationManagerBuilder터에넣넣 넣넣넣다다 필터를 합니다.SmartInitializingSingleton를 호출합니다.getObject()AuthenticationManager afterSingletonsInstantiated()★★★★★★ 。
자세한 배경은 이쪽(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/)에서 확인하실 수 있습니다.
아직 문제가 발생하고 있는 고객에게는, 여기의 수정의 예를 게재하고 있습니다.https://github.com/spring-projects/spring-security/issues/10822#issuecomment-1036063319
Spring Security 5 로의 이행의 예는, https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter#ldap-authentication 를 참조해 주세요.
@Configuration
public class SecurityConfiguration {
@Bean
@Order(1)
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// you probably want a request matcher since you are using @Order
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.apply(customDsl());
return http.build();
}
}
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilter(new TokenAuthFilter(authenticationManager));
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
언급URL : https://stackoverflow.com/questions/71281032/spring-security-exposing-authenticationmanager-without-websecurityconfigureradap
'programing' 카테고리의 다른 글
| ng-style로 div의 배경 이미지를 설정하는 방법 (0) | 2023.03.01 |
|---|---|
| Wordpress 테마 커스터마이저 - 사용자가 이동하고 위젯을 구성할 수 있는 영역 추가 (0) | 2023.03.01 |
| VARCHAR2(10 CHAR)와 NVARCHAR2(10)의 차이점 (0) | 2023.03.01 |
| Angular 명령에서 부모 컨트롤러 호출 메서드JS (0) | 2023.03.01 |
| ORA-00911: 비활성 문자 오류 해결 방법 (0) | 2023.03.01 |