我想在每次将用户添加到日志文件的请求中获取用户名。
这是我的解决方案:
首先,我创建了一个具有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个控制器,那将是很多工作。
您有更好的解决方案吗?
答案 0 :(得分:1)
Spring Security中有various ways,可以从安全上下文中获取用户详细信息。但是根据您的要求,您只对用户名感兴趣,因此您可以尝试以下操作:
@RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Authentication authentication){
logger.info(authentication.getName()); //Log username
return userService.findAllRandomCities();
}
希望这会有所帮助!
答案 1 :(得分:1)
好吧,您已经直接从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的类等触发配置新方面。 绝对值得的,因为您不仅可以记录用户名,还可以使用aspect。
答案 2 :(得分:0)
您可以通过控制器方法中的请求或参数获取用户名。如果您添加Principal principal
作为参数,Spring Ioc Container将注入有关该用户的信息,或者对于匿名用户为null。
@RequestMapping(value ="/cities")
public List<RandomCity> getCitiesAndLogWhoIsRequesting(Principal principal){
if(principal == null){
// anonymous user
}
}
答案 3 :(得分:0)
该解决方案称为鱼类标记。每个体面的日志记录框架都具有此功能。一些框架将其称为MDC
(映射的诊断上下文)。您可以阅读here和here。
基本思想是使用ThreadLocal
或InheritableThreadLocal
在线程中保留一些键值对以跟踪请求。使用日志记录配置,您可以配置如何在日志条目中打印它。
基本上,您可以编写一个过滤器,从安全上下文中检索用户名并将其放入MDC
中,而不必理会。在您的控制器中,您仅记录与业务逻辑相关的内容。用户名将与时间戳,日志级别等(根据您的日志配置)一起打印在日志条目中。
答案 4 :(得分:0)
在Jhovanni的建议下,我创建了一个像这样的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