Весна безопасности и маркер проверки подлинности для API
Я рыскал по сети, рассматривая множество различных способов реализации аутентификации на основе маркеров с помощью Spring & Spring Security (SS). Я действительно не хочу идти полным маршрутом Oauth, поэтому я пытался сделать что-то и сохранить вещи довольно простыми.
Я хочу передать имя пользователя / пароль встроенному механизму SS и при успешном выполнении сгенерировать маркер, который я передам обратно пользователю. Затем пользователь делает все последующие запросы с маркером в пользовательском заголовке. Знак истечет через некоторое время. Я знаю, что это своего рода то, что делает Oauth, но опять же, не хочу использовать его.
Значит, у меня что-то вроде работы. Я могу войти в систему с именем пользователя/паролем и получить токен обратно. Затем я могу успешно выполнять запросы с помощью маркера. То, что не работает, - это власть. Вот что я делаю...
- пользовательский обработчик ошибок Auth, который просто возвращает
HttpServletResponse.SC_UNAUTHORIZED
- пользовательский обработчик успеха, который просто возвращает
HttpServletResponse.SC_OK
и токен - пользовательская точка входа Auth, которая просто отвечает с
HttpServletResponse.SC_UNAUTHORIZED
Теперь у меня также есть пользовательские UserDetails и UserDetailsService.
public class MyUserDetails implements UserDetails {
private User user; // this is my own User object
private List<GrantedAuthority> authorities;
public MyUserDetails(User user, List<GrantedAuthority> authorities) {
this.user = user;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void setAuthorities(List<GrantedAuthority> authorities) {
this.authorities = authorities;
}
}
@Service
public class MyUserDetailsService implements UserDetailsService {
private final UserService userService;
@Autowired
public MyUserDetailsService(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
List<GrantedAuthority> authorities = new ArrayList<>();
// for now, just add something
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new MyUserDetails(user, authorities);
}
}
Чтобы посмотреть в заголовке на токен и сказать spring, что все хорошо, я создал AuthTokenFilter...
public class AuthTokenFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
private MyUserDetailsService userDetailsService;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader("X-TOKEN-AUTH");
String username = null;
if (authToken != null) {
username = Jwts.parser()
.setSigningKey("1234")
.parseClaimsJws(authToken)
.getBody()
.getSubject();
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// TODO: validate token
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}
И вот как я настроил веб-безопасность:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
@Autowired
private RestAuthEntryPoint authenticationEntryPoint;
@Autowired
private AuthSuccessHandler authSuccessHandler;
@Autowired
private AuthFailureHandler authFailureHandler;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(new ShaPasswordEncoder());
return authenticationProvider;
}
@Bean
public AuthTokenFilter authenticationTokenFilterBean() throws Exception {
AuthTokenFilter authenticationTokenFilter = new AuthTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider())
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin()
.permitAll()
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(authSuccessHandler)
.failureHandler(authFailureHandler)
.and()
.logout()
.permitAll()
.and()
.sessionManagement()
.maximumSessions(1);
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests().anyRequest().authenticated();
}
}
Кажется, все работает, но SS не ограничивает доступ вообще. Если маркер есть, SS просто пропускает все.