Как получить текущего пользователя в каждом запросе в Spring Boot? - PullRequest
0 голосов
/ 20 октября 2018

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

Это мое решение:

Сначала я создалLoggedUser со свойством static:

public class LoggedUser {

    private static final ThreadLocal<String> userHolder = 
        new ThreadLocal<>();

    public static void logIn(String user) {
        userHolder.set(user);
    }

    public static void logOut() {
        userHolder.remove();
    }

    public static String get() {
        return userHolder.get();
    }
}

Затем я создал класс поддержки для получения имени пользователя:

public interface AuthenticationFacade {
    Authentication getAuthentication();
}
@Component
public class AuthenticationFacadeImpl implements AuthenticationFacade {
    @Override
    public Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}

Наконец, я использовал их в своих контроллерах:

    @RestController
    public class ResourceController {

        Logger logger = LoggerFactory.getLogger(ResourceController.class);

        @Autowired
        private GenericService userService;
        @Autowired
        private AuthenticationFacade authenticationFacade;

        @RequestMapping(value ="/cities")
        public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
        loggedUser.logIn(authenticationFacade.getAuthentication().getName());
        logger.info(LoggedUser.get()); //Log username
        return userService.findAllRandomCities();
        }
    }

Проблема в том, что я не хочу иметь AuthenticationFacade в каждом @Controller. Если у меня, например, 10000 контроллеров, будет много работы.

У вас есть лучшее решение для этого?

Ответы [ 5 ]

0 голосов
/ 21 октября 2018

По предложению Джованни я создал аннотацию AOP, например:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogUsername {
}

В том же пакете я добавил новый класс @Aop @Component с AuthenticationFacade инъекцией:

@Aspect
@Component
public class LogUsernameAop {
    Logger logger = LoggerFactory.getLogger(LogUsernameAop.class);
    @Autowired
    private AuthenticationFacade authenticationFacade;

    @Before("@annotation(LogUsername)")
    public void logUsername() throws Throwable {
        logger.info(authenticationFacade.getAuthentication().getName());
        LoggedUser.logIn(authenticationFacade.getAuthentication().getName());
    }
}

Затем, в каждом методе @GetMapping. Если мне нужно зарегистрировать имя пользователя, я могу добавить аннотацию перед методом:

@PostMapping
@LogUsername
public Course createCourse(@RequestBody Course course){
    return courseService.saveCourse(course);
}

Наконец, это результат:

2018-10-21 08:29:07.206  INFO 8708 --- [nio-8080-exec-2] com.khoa.aop.LogUsername                 : john.doe
0 голосов
/ 20 октября 2018

Итак, вы уже получаете доступ к объекту аутентификации непосредственно из SecurityContextHolder, вы можете сделать это в своем контроллере.

@RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  if(authentication != null){
    //log user name
    logger.info(authentication.get());
  }
  return userService.findAllRandomCities();
}

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

public class UserUtil {
  public static String userName(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    return authentication == null ? null : authentication.getName();
  }
}

и вызова его в вашей конечной точке как

@RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
  //log user name
  logger.info(UserUtil.username());
  return userService.findAllRandomCities();
}

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

@RequestMapping(value ="/cities")
@LogUserName
public List<RandomCity> getCitiesAndLogWhoIsRequesting(){
  //LogUserName annotation will inform this request should log user name if found
  return userService.findAllRandomCities();
}

Конечно, вы можете удалить пользовательскую аннотацию @LogUserName и настроить новый аспект так, чтобы он вызывался методами внутри пакета или классами, расширяющими @Controller,и т. д. Определенно стоит потратить время, потому что вы можете использовать аспекты не только для регистрации имени пользователя.

0 голосов
/ 20 октября 2018

Решение называется Fish Tagging .Каждый приличный каркас имеет такую ​​функциональность.Некоторые платформы называют это MDC (сопоставленный диагностический контекст).Вы можете прочитать об этом здесь и здесь .

Основная идея заключается в использовании ThreadLocal или InheritableThreadLocal для хранения нескольких пар ключ-значение впоток для отслеживания запроса.Используя конфигурацию ведения журнала, вы можете настроить способ печати в записях журнала.

По сути, вы можете написать фильтр, в котором вы извлекаете имя пользователя из контекста безопасности и помещаете его в MDC и простозабудь об этом.В вашем контроллере вы регистрируете только вещи, связанные с бизнес-логикой.Имя пользователя будет напечатано в записях журнала вместе с отметкой времени, уровнем журнала и т. Д. (В соответствии с вашей конфигурацией журнала).

0 голосов
/ 20 октября 2018

В Spring Security есть различные способы для получения сведений о пользователе из контекста безопасности.Но по вашему требованию вас интересует только имя пользователя , поэтому вы можете попробовать это:

@RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Authentication authentication){
    logger.info(authentication.getName()); //Log username
    return userService.findAllRandomCities();
}

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

0 голосов
/ 20 октября 2018

Вы можете получить имя пользователя с помощью запроса или параметра в методе вашего контроллера.Если вы добавите Principal principal в качестве параметра, Spring Ioc Container будет вводить информацию о пользователе, или она будет нулевой для анонимных пользователей.

@RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Principal principal){
    if(principal == null){
         // anonymous user
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...