#java #spring #spring-security #jwt
Вопрос:
Я работал над демонстрацией Spring Security и одновременно пытался добавить учетные данные для входа в мое существующее приложение. Все шло нормально, пока не появился более длинный раздел, в котором демо-версия заставила меня создать пользовательский фильтр аутентификации, и при загрузке страница входа в систему исчезла и возвращала 404 из отправленных запросов myserver/login
. С момента последней работы я создал следующую конфигурацию безопасности:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
public SecurityConfig(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().permitAll()
.and()
.sessionManagement().sessionCreationPolicy(STATELESS)
.and()
.formLogin().loginPage("/login")
.and()
.addFilter(new CustomAuthenticationFilter(authenticationManagerBean()));
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Я прочитал кучу вопросов и подумал, что что-то происходит с пользовательским фильтром аутентификации, и я не указал страницу входа в систему, и с тех пор я изменил эту конфигурацию на:
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().permitAll()
.and()
.sessionManagement().sessionCreationPolicy(STATELESS)
.and()
.formLogin().loginPage("/login")
.and()
.addFilter(new CustomAuthenticationFilter(authenticationManagerBean()));
}
При отладке я вижу, что выполняется конфигурация http. На данный момент ничто в пользовательском фильтре аутентификации не доступно за пределами конструктора, но в случае, если это имеет значение, код фильтра выглядит следующим образом:
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public CustomAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username,
password);
return authenticationManager.authenticate(authenticationToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authentication)
throws IOException, ServletException {
User user = (User) authentication.getPrincipal();
String access_token = createToken(user, request, response, 10);
String refresh_token = createToken(user, request, response, 60);
Map<String, String> tokens = createTokenMap(access_token, refresh_token);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
private String createToken(User user, HttpServletRequest request, HttpServletResponse response, int minutes) {
//Poor practice acknowledged... only exposed for this demo application
Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
return JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() (minutes * 60 * 1000)))
.withIssuer(request.getRequestURL().toString())
.withClaim("roles",
user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
}
private Map<String, String> createTokenMap(String access_token, String refresh_token) {
Map<String, String> tokens = new HashMap<>();
tokens.put("access_token", access_token);
tokens.put("refresh_token", refresh_token);
return tokens;
}
}
У меня есть следующие зависимости для безопасности/интернета в моей сборке gradle:
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation group: 'org.springframework.security', name: 'spring-security-config', version: '5.3.0.RELEASE'
implementation group: 'com.auth0', name: 'java-jwt', version: '3.18.1'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-test'
implementation 'org.springframework.boot:spring-boot-starter-validation'
Поскольку я запустил этот учебник, интегрировав его с моим существующим приложением, которое использует интерфейс React, я думаю, что конфликт может возникнуть из-за уже существующей маршрутизации, но для меня это не имеет особого смысла, так как страница входа обслуживалась до тех пор, пока я не внедрил пользовательский фильтр аутентификации.
На данный момент я попытался вернуть кучу кода, чтобы попытаться вернуться к тому моменту, когда у меня есть страница входа в систему, но ничего не сработало. Есть ли что-нибудь, что я могу добавить, чтобы вернуть функциональность spring по умолчанию myserver/login
? Я бы очень хотел поиграть с запросами на токены/и т. Д. И, В конечном счете, изучить базовую регистрацию для этого приложения.
Дайте мне знать, если может потребоваться какой-либо другой код. Спасибо!
Комментарии:
1. Трудно сделать предложение. Можете ли вы попробовать добавить «.PermitAll()» после «.formLogin().Страница входа («/вход»)»? Так и будет «.formLogin().Страница входа(«/вход»).PermitAll()»
2. При использовании
loginPage
dsl вы сообщаете Spring Security, что у вас есть пользовательская страница входа. Если вы хотите использовать страницу входа по умолчанию, вы можете опуститьloginPage
, например:.formLogin().and()...
3. К сожалению, ни то, ни другое не имело значения. Приложение по-прежнему не защищено логином. Кроме того, когда я начал с Spring Security, он дал мне пароль по умолчанию, и он больше этого не делает. Хотя в аннотации конфигурации есть Spring, выполняемая через конфигурацию http, создается впечатление, что безопасность полностью отключена.
4. Вы указываете
authorizeRequests().anyRequest().permitAll()
, что означает, что все запросы разрешены без аутентификации. Может быть, вам стоит попробовать использоватьauthorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll()
5. прежде всего, вы устанавливаете
.formLogin().loginPage("/login")
, что означает, что spring boot ожидает, что вы предоставите страницу входа в систему в этом месте. Вы там что-нибудь подаете? Во-вторых, не могли бы вы также предоставить свои журналы отладки. В-третьих, передача имени пользователя и пароля в качестве параметров запроса чрезвычайно опасна, кроме того, фильтры обычно не используются в качестве точки входа в систему. фильтры используются для проверки людей, которые предоставляют токены или файлы cookie сеанса.
Ответ №1:
Переходим authorizeRequests().anyRequest().permitAll()
на authorizeRequests().anyRequest().authenticated().and().formLogin().permitAll()
страницу входа в систему! Я опустил указание страницы, поскольку у меня еще нет пользовательской, но эта конфигурация привела меня к тому, что я могу использовать учетные данные для входа, что было моим первоначальным вопросом, поэтому я публикую это как ответ. Последующий вопрос в комментариях.