Весна безопасности и маркер проверки подлинности для 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 просто пропускает все.

1 2

1 ответ:

Ну, после долгих проб и ошибок это было так же просто, как добавить следующее К моему SpringSecurityConfig

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)

Немного удивлен, что я не столкнулся с этим раньше. Не уверен, что это что-то новое или что-то еще.