Используйте JsonView для выборочного скрытия полей - PullRequest
0 голосов
/ 31 марта 2020

Я пытаюсь выборочно скрыть определенные поля в ответе, в зависимости от роли запрашивающего его пользователя. Из того, что я смог понять, JsonView из Jackson может быть путем к go.

Мне нужно иметь возможность отображать все поля, ЕСЛИ они не были помечены с указанием c уровень доступа. Я создал следующую структуру для уровней доступа:

(Быстрое примечание: я оставил там базового пользователя, но это не должно иметь большого значения)

public class View {
    public static final Map<Role, Class> MAPPING = new HashMap<>();

    static {
        MAPPING.put(Role.ROLE_ADMIN, Admin.class);
        MAPPING.put(Role.ROLE_USER, AuthenticatedUser.class);
    }

    public interface User {}
    public interface AuthenticatedUser extends User {}
    public interface Admin extends AuthenticatedUser {}
}

И использовал их следующим образом:

@Entity
public class SomeEntity {
    @Id
    @GeneratedValue
    private Integer id;

    private String baseInfo;

    @JsonView(View.AuthenticatedUser.class)
    private String userInfo;

    @JsonView(View.Admin.class)
    private String secretInfo;

    ...
}

(PS: я удалил все несущественные аннотации и т. д. c.)

Теперь я ожидаю, что в зависимости от уровня доступа, Ответы следующие:

  1. Неаутентифицированный пользователь:
    {
        "id": 1,
        "baseInfo": "Some basic info"
    } 
    
  2. Аутентифицированный пользователь:
    {
        "id": 1,
        "baseInfo": "Some basic info",
        "userInfo": "Info only the user should be able to see"
    } 
    
  3. Администратор:
    {
        "id": 1,
        "baseInfo": "Some basic info",
        "userInfo": "Info only the user should be able to see",
        "secretInfo": "Info only the admin can see"
    } 
    

Я использовал код из этого учебника, чтобы интегрировать его в Spring Security и мою собственную структуру.

@RestControllerAdvice
class SecurityJsonViewControllerAdvice extends AbstractMappingJacksonResponseBodyAdvice {

    @Override
    protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
        if (SecurityContextHolder.getContext().getAuthentication() != null && SecurityContextHolder.getContext().getAuthentication().getAuthorities() != null) {
            Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
            List<Role> foundRoles = authorities.stream()
                    .map(GrantedAuthority::getAuthority)
                    .map(Role::get).collect(Collectors.toList());

            if (foundRoles.contains(null)) {
                System.err.println("User has no auth. Setting no serialization view");
                return;
            }

            List<Class> jsonViews = foundRoles.stream().map(View.MAPPING::get)
                    .collect(Collectors.toList());
            if (jsonViews.size() == 1) {
                System.err.println("Setting " + jsonViews.get(0) + " as serialization view");
                bodyContainer.setSerializationView(jsonViews.get(0));
                return;
            }
            throw new IllegalArgumentException("Ambiguous @JsonView declaration for roles "
                    + authorities.stream()
                    .map(GrantedAuthority::getAuthority).collect(Collectors.joining(",")));
        }
        System.err.println("No auth found");
    }
}

(Простите за ужасный код, я пытался немного все ...)

На данный момент, я ожидаю, что результат будет таким, как я говорил выше, но я продолжаю получать все поля, без какой-либо фильтрации. Ни один тип аннотации не исключает сериализацию полей. Я знаю, что по умолчанию я могу установить, что все поля исключены, КРОМЕ тех, которые я аннотирую, но это будет означать аннотирование всех полей, и проект довольно большой.

Мое первоначальное предположение о JsonView неверно , или я где-то ошибаюсь?

Заранее спасибо

1 Ответ

1 голос
/ 01 апреля 2020

Благодаря комментарию @second мне удалось выяснить проблему. Как оказалось, я допустил две небольшие ошибки:

1. Настройка Джексона на включение всех свойств по умолчанию

Первое изменение - установка spring.jackson.mapper.default_view_inclusion на true в вашем файле application.properties. Таким образом, Джексон по умолчанию будет включать все свойства, устраняя проблему, когда полезная нагрузка пуста

2. Использование «базового» пользователя

Всякий раз, когда неаутентифицированный пользователь пытается получить доступ к системе, нам необходимо установить представление сериализации для базового пользователя. В моем посте выше у меня есть User View, но я никогда не использовал его. Установка его в качестве представления сериализации по умолчанию (в перехватчике выше) решает проблему, и результатом является тот, который я указал выше.

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

Я загрузил пример здесь, Это очень просто, но показывает, как можно исключить различные поля. Используйте вот так:

  • http://localhost:8080/test
    {
        "id":1,
        "baseInfo":"This is the base info"
    }
    
  • http://localhost:8080/test?view=user
    {
        "id":1,
        "baseInfo":"This is the base info",
        "userInfo":"This is the user info"
    }
    
  • http://localhost:8080/test?view=admin
    {
        "id":1,
        "baseInfo":"This is the base info",
        "userInfo": "This is the user info",
        "secretInfo":"This is the secret info"
    }
    

Надеюсь, это поможет!

...