Понимание «Отказано в доступе» в весенней безопасности с пользовательской аутентификацией - PullRequest
0 голосов
/ 25 августа 2018

Я пытаюсь понять Spring Security и после некоторой работы (и помощи по SO) я смог реализовать какой-то специальный механизм аутентификации в весенней безопасности, но теперь у меня возникли некоторые проблемы с авторизацией, которые я не совсем понимаю.

Если я сделаю POST-запрос к localhost: 8080 / login? Username = admin & password = sesamOeffneDich & secondSecret = youNeedMe Я получу 403 отказа в доступе.

Если бы кто-то мог объяснить почему, я был бы благодарен.

Моя конфигурация следующая:

@Configuration
@EnableWebSecurity
@EnableWebMvc
@ComponentScan
public class AppConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    MyAuthenticationProvider myAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(myAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.addFilterBefore(new MyAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class)
                .authorizeRequests().antMatchers("/**")
                    .hasAnyRole()
                    .anyRequest()
                    .authenticated()
                    .and()
                .csrf().disable()
                .httpBasic().disable();
    }

    @Bean
    public AuthenticationManager authenticationManager(){
        return new ProviderManager(Arrays.asList(myAuthenticationProvider));
    }

    @Bean
    public ViewResolver viewResolver()
    {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

Я думал, что с этой конфигурацией механизм авторизации автоматически предоставит доступ из-за [...]. HasAnyRole () [...] .

Это моя реализация токена; Я хотел сделать это простым для практических целей; если какая-либо другая часть реализации необходима для понимания, пожалуйста, скажите мне, и я предоставлю это, но я не хотел первоначально наполнять пост ненужным кодом слишком много:

public class MyAuthenticationToken implements Authentication {
    public static final String SECOND_SECRET = "youNeedMe";
    private final String principalName;
    private MyCredentials credentials;
    private boolean authenticated;

    public MyAuthenticationToken(String principalName, MyCredentials credentials) {
        this.principalName = principalName;
        this.credentials = credentials;
    }
    //Everyone is admin for the sake of the example
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        LinkedList<GrantedAuthority> authorities = new LinkedList<>();
        authorities.add(new SimpleGrantedAuthority("ADMIN"));
        return authorities;
    }

    @Override
    public Object getCredentials() {
        return this.credentials;
    }

    @Override
    public Object getDetails() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principalName;
    }

    @Override
    public boolean isAuthenticated() {
        return this.authenticated;
    }

    @Override
    public void setAuthenticated(boolean b) throws IllegalArgumentException {
        this.authenticated = b;
    }

    @Override
    public String getName() {
        return this.principalName;
    }
}

РЕДАКТИРОВАТЬ: по запросу я добавляю дополнительные источники.

public class MyAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public MyAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(new AntPathRequestMatcher("/login", "POST"));
        this.setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
            if (!request.getMethod().equals("POST")) {
                throw new AuthenticationServiceException(
                        "Authentication method not supported: " + request.getMethod());
            }

            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String secondSecret = request.getParameter("secondSecret");

            if (username == null) {
                username = "";
            }

            if (password == null) {
                password = "";
            }

            username = username.trim();

            MyAuthenticationToken authRequest = new MyAuthenticationToken(username, new MyCredentials(password, secondSecret));

            return this.getAuthenticationManager().authenticate(authRequest);
    }
}

И

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MyAuthenticationToken myAuthenticationToken = (MyAuthenticationToken) authentication;
        MyCredentials credentials = (MyCredentials) myAuthenticationToken.getCredentials();
        if (credentials.getPassword().equals("sesamOeffneDich") && credentials.getSecondSecret().equals(MyAuthenticationToken.SECOND_SECRET)){
            myAuthenticationToken.setAuthenticated(true);
            return myAuthenticationToken;
        }else{
            throw new BadCredentialsException("Bad credentials supplied!");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return MyAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

1 Ответ

0 голосов
/ 25 августа 2018

Для обработки вашего вопроса мне нужен источник MyAuthenticationFilter и MyAuthenticationProvider.Но у меня есть лучшее решение, которое реализовывалось ранее, вы можете найти полностью работоспособный пример пружинной безопасности, настроенный с помощью Custom Authentication и RBAC (Role-based Access Control), на моем github, готовом для вас: https://github.com/mehditahmasebi/spring/tree/master/spring-boot

Я надеюсь, что это поможет вам.


Мой WebConfig (или, как вы сказали, AppConfig) Источники:

@Configuration
@EnableWebMvc
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        prePostEnabled=true,
        securedEnabled=true,
        jsr250Enabled=true)
public class WebConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {

    @Override
    protected void configure(HttpSecurity http) throws Exception {      
        http
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            .and().csrf().disable();
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
        .allowCredentials(true)
        .allowedHeaders("*")
        .allowedMethods("GET, POST, PATCH, PUT, DELETE, OPTIONS")
        .allowedOrigins("*");
    }

    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver resolver= new InternalResourceViewResolver();
        resolver.setPrefix("/jsp/");
        resolver.setSuffix(".jsp");
        return resolver;
    }  

    @Bean
    public ErrorPageFilter errorPageFilter() {
        return new ErrorPageFilter();
    }
}

и источник CustomAuthentication:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    public CustomAuthenticationProvider() {
        super();
    }

    // API

    @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
        final String name = authentication.getName();
        final String password = authentication.getCredentials().toString();
        final List<GrantedAuthority> grantedAuths = new ArrayList<>();

        if(!(name.equals("admin") && password.equals("admin")) && 
                !(name.equals("user") && password.equals("user")))
            throw new RuntimeException("username or password is incorrect");
        if(name.equals("admin"))
        {
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }
        else
            grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));

        final UserDetails principal = new User(name, password, grantedAuths);
        final Authentication auth = new UsernamePasswordAuthenticationToken(principal, password, grantedAuths);
        return auth;

    }

    @Override
    public boolean supports(final Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

и источник LoginService:

@RestController
@RequestMapping("/public/login")
public class LoginService {

    @Autowired
    CustomAuthenticationProvider provider;

    @GetMapping
    public String loginTest(){
        return "Login OK";
    }

    @GetMapping("/{username}/{password}")
    public ResponseEntity<Map<String, String>> login(@PathVariable String username,@PathVariable String password ,HttpServletRequest request)
    {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authenticate = provider.authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(authenticate);
        request.getSession().setAttribute("username", username);
        Map<String, String> result = new HashMap<>();
        result.put(username, new String(Base64.getEncoder().encode(password.getBytes())));
        ResponseEntity<Map<String, String>> finalResult = new ResponseEntity<>(result,HttpStatus.OK);
        return finalResult;
    }
}

Для запуска проекта загрузите его и запустите:

mvnw spring-boot: запустите

, а затем -> http://localhost:8080/public/login/admin/admin

тогда вы получите успешный результат, как хотите.

Приветствия.


РЕДАКТИРОВАТЬ:

После обновления вопроса с добавленным источником, я обнаружил проблему.

Есть какое-то решение:

изменить / ** на / login

    http.addFilterBefore(new MyAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class)
            .authorizeRequests()
                .antMatchers("/login").hasAnyRole()
                .anyRequest().authenticated()
            .and()
            .csrf().disable()
            .httpBasic().disable();

Или самым простым способом удалить свои antMatchers **:

    http.addFilterBefore(new MyAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class)
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
            .csrf().disable()
            .httpBasic().disable();

На самом деле, если вы хотите проверить роль, не рекомендуется проверять роль в службе регистрации, лучше сначала войти в систему, а затем проверять роль следующего запроса (другого конкретного antMatcherUrl).

В то же время выможно использовать @Secured для авторизации роли.

Для обеспечения безопасности я добавил службу Hello для обеспечения безопасности исключительной ситуации до входа в систему, а после входа в систему все идет хорошо.

@RestController("/hello")
public class HelloService {

    @GetMapping
    public String get()
    {
        return "Hello there!";
    }

}
...