Спринг-сопоставитель муравьев безопасности не ограничивает конечную точку указанной ролью

avatar
Dolphy the Reaper
9 августа 2021 в 05:58
298
1
0

Я использую Spring Security с механизмом JWT. У меня есть эта весенняя конфигурация безопасности:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/login", "/users/**").permitAll()
            .antMatchers(PUT, "/answers/(\\d+)").hasAnyAuthority("ROLE_INSTRUCTOR")
            .antMatchers(GET, "/answers/(\\d+)/is-correct").hasAnyAuthority("ROLE_STUDENT")
            .anyRequest().authenticated()
            .and()
            .addFilter(new CustomAuthenticationFilter(authenticationManagerBean()))
            .addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .csrf().disable();
}

Поскольку я использую JWT, у меня есть 2 фильтра. Один для аутентификации:

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        log.info("Username is: {}", username);
        log.info("Password is: {}", 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();
        Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
        String accessToken = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + 2 * 60 * 1000)).withIssuer(request.getRequestURL().toString())
                .withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                .sign(algorithm);
        String refreshToken = JWT.create()
                .withSubject(user.getUsername())
                .withExpiresAt(new Date(System.currentTimeMillis() + 5 * 60 * 1000)).withIssuer(request.getRequestURL().toString())
                .sign(algorithm);
        Map<String, String> headerTokens = new HashMap<>();
        headerTokens.put("access_token", accessToken);
        headerTokens.put("refresh_token", refreshToken);
        response.setContentType(APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getOutputStream(), headerTokens);
    }
}

А другой для авторизации:

public class CustomAuthorizationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String servletPath = request.getServletPath();
        if ("/users/register".equals(servletPath) || "/login".equals(servletPath) || "/users/refresh-token".equals(servletPath)) {
            filterChain.doFilter(request, response);
        } else { //here start the checks for authorizationn
            String authorizationHeader = request.getHeader(AUTHORIZATION);
            if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
                try {
                    String token = authorizationHeader.substring("Bearer ".length());
                    Algorithm algorithm = Algorithm.HMAC256("secret".getBytes());
                    JWTVerifier jwtVerifier = JWT.require(algorithm).build();
                    DecodedJWT decodedJWT = jwtVerifier.verify(token);
                    String username = decodedJWT.getSubject();
                    String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
                    Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
                    stream(roles).forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    filterChain.doFilter(request, response);
                } catch (Exception exception) {
                    log.error("Error logging in: {}", exception.getMessage());
                    response.setStatus(FORBIDDEN.value());
                    Map<String, String> error = new HashMap<>();
                    error.put("error_message", exception.getMessage());
                    response.setContentType(APPLICATION_JSON_VALUE);
                    new ObjectMapper().writeValue(response.getOutputStream(), error);
                }
            } else {
                filterChain.doFilter(request, response);
            }
        }
    }
}

Проблема в том, что когда я нажимаю запрос GET с ролью INSTRUCTOR, запрос все равно принимается. Это должно было быть отклонено весенней безопасностью, но оно было принято. И если я нажму запрос PUT с ролью STUDENT, запрос все равно будет принят. Я что-то пропустил?

Источник
Toerktumlare
9 августа 2021 в 14:30
0

Во-первых, где находятся ваши журналы отладки, во-вторых, написание пользовательской безопасности - это плохая практика, существуют стандарты безопасности, и если вы используете инфраструктуру безопасности, вы должны использовать фильтры, которые предлагает инфраструктура. Если вы пишете собственные фильтры, вы действуете самостоятельно и не ожидаете, что функции фреймворка будут работать, так как вы по существу «отказались» от использования фреймворка.

Toerktumlare
9 августа 2021 в 14:32
0

Spring security имеет полную поддержку jwt уже 3 года, я бы посоветовал вам прочитать справку вместо того, чтобы пытаться создать что-то нестандартное.

Dolphy the Reaper
9 августа 2021 в 15:53
0

Я знаю, что у него есть поддержка, но это мой проект, в котором я хочу узнать что-то новое. Дело в том, что я использовал @PreAuthorize("hasRole('STUDENT')") и @PreAuthorize("hasRole('INSTRUCTOR')"), и кажется, что этот способ работает, а также я добавил .antMatchers("/** ").authenticated() в конфигурации безопасности Spring. Что-то происходит с antMatchers, что как-то не в моей лиге...

Ответы (1)

avatar
Eleftheria Stein-Kousathana
10 августа 2021 в 07:43
0

Вы передаете регулярное выражение ("/answers/(\\d+)") для сопоставления Ant.

.antMatchers(PUT, "/answers/(\\d+)")

Если вы не хотите изменять выражение, вы можете использовать средство сопоставления регулярных выражений.

.regexMatchers(PUT, "/answers/(\\d+)")

В противном случае необходимо указать шаблон пути муравья.