【问题标题】:Spring: get current user through injectionSpring:通过注入获取当前用户
【发布时间】:2017-06-08 12:32:46
【问题描述】:

目前我正在以通常的方式吸引用户

SecurityContextHolder.getContext().getAuthentication().getPrincipal

不知道能不能通过bean factory得到,像这样

@Service
class UserProvider implements BeanFactoryAware {
    BeanFactory beanFactory;

    public Principal get(){
        return (Principal) beanFactory.getBean("Principal");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
} 

Spring 是否存储包含当前已验证用户的会话范围 bean?

【问题讨论】:

  • 如果你告诉它存储它就可以了。或者如果其他一些 bean 依赖它,我猜。
  • 这是不可能的。您可以在 Spring MVC 控制器中使用 Principal 作为方法参数,但不能使用 @Autowire 它。 (这也是一个坏主意恕我直言)。您可以创建一个简单的帮助类来进行查找并从您需要用户的任何地方(在 MVC 部分之外)调用它。
  • 你为什么需要这个?

标签: java spring


【解决方案1】:

您可以将自定义 bean 后处理器与动态代理结合使用来实现此目的。

类似:

@SpringBootApplication
public class So44435897Application {

    public static void main(String[] args) {
        SpringApplication.run(So44435897Application.class, args);
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface ApplicationUser { }

    @Component
    public static class CurrentUserBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            ReflectionUtils.doWithFields(bean.getClass(), field -> {
                ReflectionUtils.makeAccessible(field);
                if (field.getAnnotation(ApplicationUser.class) != null) {
                    final Object proxyInstance = Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                            new Class[] { UserDetails.class }, (proxy, method, args) -> {
                                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                                if (authentication != null && authentication.isAuthenticated()) {
                                    final Object principal = authentication.getPrincipal();
                                    return method.invoke(principal, args);
                                }
                                throw new NullPointerException();
                            });
                    field.set(bean, proxyInstance);
                }
            });
            return bean;
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }

    @RestController
    public static class HomeController {
        final UserService userService;

        @Autowired
        public HomeController(UserService userService) {
            this.userService = userService;
        }

        @GetMapping
        public String me() {
            return userService.getName();
        }
    }

    @Service
    public static class UserService {
        @ApplicationUser
        UserDetails user;

        String getName() {
            return user.getUsername();
        }
    }
}

=>

$ curl -u admin:admin -XGET 'localhost:8080'
admin%

【讨论】:

    【解决方案2】:

    可以定义会话范围的 bean。

    定义

    @Component
    @Scope("session") 
    public class User { 
        init 
    }
    

    用法

    @Service
    public UserService {
        @Autowire
        User user;
    }
    

    注意如果您将会话范围的 bean 注入到非范围的 bean,请注意代理

    @Scope(value="session", proxyMode=ScopedProxyMode.???)
    

    【讨论】:

    • 其实我想注入 Spring 已经提供的 Principal,而不定义我自己的 User bean
    • 因此,如果您将作用域 bean 注入到非作用域中,请注意代理
    • 服务不应该是无状态的吗?
    猜你喜欢
    • 2019-02-02
    • 1970-01-01
    • 2014-06-10
    • 2017-12-20
    • 2018-10-25
    • 2018-11-21
    • 2021-03-30
    • 2018-08-17
    • 1970-01-01
    相关资源
    最近更新 更多