My session scoped bean:
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
#Component
public class AuthNodes {
private String authNodes;
public String getAuthNodes() {
return authNodes;
}
public void setAuthNodes(String authNodes) {
this.authNodes = authNodes;
}
}
is injected in a REST controller of a JHipster generated microservice:
#RestController
#RequestMapping("/api")
public class NodeResource {
#Autowired
private AuthNodes authNodes;
...
#GetMapping("/nodes-and-children/{user:.+}")
#Timed
public ResponseEntity<List<Node>> getFilteredNodesAndChildren(#PathVariable String user,
#ApiParam Pageable pageable) {
...
String hosts = authNodes.getAuthNodes();
if (hosts == null) {
authNodes.setAuthNodes("my user's authorized node names");
}
...
but at each call the previously set value is lost and authNodes.getAuthNodes() returns null.
What is wrong?
Thanks, Mic
Related
#Service
public class UserService {
private String name;
//fields omitted
//#Cacheable(value = "user", key = "#name") //once added, the name will be null.
public User getUser(String name) {
}
}
#Service
class UserServiceBuilder(){
public UserService build(ConfigBean config){
UserService s = new UserServcie()
s.name = config.xxxx
//other config omitted
return s;
}
}
#Configuration
class AppConfig{
#Bean
public UserService UserService(UserServiceBuilder builder, ConfigBean configBean) {
return builder.load(configBean);
}
}
class UserCtrl {
#Autowired
private UserService UserService; // get null when the #Cachable
}
UserService is created by the UserServiceBuilder which will read a log of properties from the config file.
Then the UserService will be injected to UserCtrl, it works at first.
However once I add the #Cachable to one method of UserService, all the fileds of the injected UserService will be null.
It seems like that spring will create a proxy of UserService when using cache, and the proxy object does not have the fileds.
How to fix that?
Yes, you are right it's because of the proxy. You must add getters in the UserService and use this getters if you want take the fields of the UserService outside.
#Service
public class UserService {
private String name;
//fields omitted
//#Cacheable(value = "user", key = "#name") //once added, the name will be null.
public User getUser(String name) {
}
//ADD THIS:
public String getName() {
return this.name;
}
}
But if you add output inside UserService method:
public User getUser(String name) {
System.out.println("PING " + this.name);
...
}
you will see that this.name inside object is not null.
P.S. and I think that you can remove #Service annotation from UserService. Because you have #Service and #Bean registration of UserService. It's confusing.
I created Spring boot application with the following configuration:
Spring boot 2.1.0.RELEASE
OpenJdk 11
I have an AuditConfiguration class in my project that looks like:
#Configuration
#EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class AuditConfiguration {
#Bean
public AuditorAware<String> auditorProvider() {
return new AuditorAwareImpl();
}
class AuditorAwareImpl implements AuditorAware<String> {
#Override
public Optional<String> getCurrentAuditor() {
Principal principal =
SecurityContextHolder.getContext().getAuthentication();
return Optional.of(principal.getName());
}
}
}
and SecurityContextHolder.getContext().getAuthentication() always returns anonymousUser.
However, the following code returns the correct user name.
#RestController
#RequestMapping("/history")
public class HistoryEndpoint {
#RequestMapping(value = "/username", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Principal principal) {
return principal.getName();
}
}
I need your help for resolving this issue.
I got authenticared user using following class. i had problem with JPA Auditing.
#CreatedBy always saved null. then i tried to get authenticated user SecurityContextHolder.getContext().getAuthentication() using this method. that method returned annonymousUser. however my issue is fixed.
#ManagedBean
#EnableJpaAuditing
public class SpringSecurityAuditorAware implements AuditorAware<String> {
private final HttpServletRequest httpServletRequest;
public SpringSecurityAuditorAware(HttpServletRequest httpServletRequest) {
this.httpServletRequest = httpServletRequest;
}
#Override
public Optional<String> getCurrentAuditor() {
return Optional.ofNullable(httpServletRequest.getUserPrincipal())
.map(Principal::getName);
}
}
I have a REST controller with a GET method. It returns a resource. I want to verify if the resource belongs to the authorized user by comparing the owner field on the Resource with the authorized user's login. With a normal synchronous request I'd do something like this:
#RestController
#RequestMapping("/api")
public class AController {
private final AService aService;
public AController(AService aService) {
this.aService = aService;
}
#GetMapping("/resources/{id}")
#PostAuthorize("returnObject.ownerLogin == authentication.name")
public Resource getResource(#PathVariable Long id) {
return aService.getResource(id);
}
}
But what if the controller method is asynchronous (implemented with DeferredResult)?
#RestController
#RequestMapping("/api")
public class AController {
private final AService aService;
public AController(AService aService) {
this.aService = aService;
}
#GetMapping("/resources/{id}")
#PostAuthorize("returnObject.ownerLogin == authentication.name")
public DeferredResult<Resource> getResource(#PathVariable Long id) {
DeferredResult<Resource> deferredResult = new DeferredResult<>();
aService
.getResourceAsync(id)
.thenAccept(resource -> {
deferredResult.setResult(resource);
});
return deferredResult;
}
}
Where AService interface looks like this:
#Service
public class AService {
#Async
public CompletableFuture<Resource> getResourceAsync(Long id) {
// implementation...
}
public Resource getResource(Long id) {
// implementation...
}
}
And Resource class is a simple DTO:
public class Resource {
private String ownerLogin;
// other fields, getters, setters
}
In the second example Spring Security obiously looks for the ownerLogin field on the DeferredResult instance. I'd like it to treat the asynchronously resolved Resource as the returnObject in the #PostAuthorize SPEL expression.
Is it possible? Maybe someone can suggest an alternatve approach? Any suggestions are welcome.
Couldn't achieve my goal with PostAuthorize and endedd up doing the following:
Made Resource a subresource of the User resource. Used a PreAuthorize annotation to validate user's login.
#RestController
#RequestMapping("/api")
public class AController {
private final AService aService;
public AController(AService aService) {
this.aService = aService;
}
#GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}/resources/{id}")
#PreAuthorize("#login == authentication.name")
public DeferredResult<Resource> getResource(#PathVariable String login, #PathVariable Long id) {
DeferredResult<Resource> deferredResult = new DeferredResult<>();
aService
.getResourceAsync(login, id)
.thenAccept(resource -> {
deferredResult.setResult(resource);
});
return deferredResult;
}
}
Added an ownership check in AService. If Resource owner and the requesting user's login don't match throw an Exception that resolves to a 404 HTTP status:
#Service
public class AService {
private final ARepository aRepository;
public AController(ARepository aRepository) {
this.aRepository = aRepository;
}
#Async
public CompletableFuture<Resource> getResourceAsync(String owner, Long id) {
Resource resource = aRepository.getResource(id);
if (!resource.owner.equals(owner)) {
// resolves to 404 response code
throw ResourceNotFounException();
}
return resource;
}
}
I was trying to use spring stater-cache in spring boot 1.3.5, everything works fine except pre load cache in #Configuration class.
Failed tests:
CacheTest.testCacheFromConfig: expected:<n[eal]> but was:<n[ot cached]>
Please take a look at the code as below, if you met this before, please share it with me :)
#Component
public class CacheObject{
#CachePut(value = "nameCache", key = "#userId")
public String setName(long userId, String name) {
return name;
}
#Cacheable(value = "nameCache", key = "#userId")
public String getName(long userId) {
return "not cached";
}
}
#Component
public class CacheReference {
#Autowired
private CacheObject cacheObject;
public String getNameOut(long userId){
return cacheObject.getName(userId);
}
}
#Configuration
public class SystemConfig {
#Autowired
private CacheObject cacheObject;
#PostConstruct
public void init(){
System.out.println("------------------");
System.out.println("-- PRE LOAD CACHE BUT DIDN'T GET CACHED");
System.out.println("------------------");
cacheObject.setName(2, "neal");
cacheObject.setName(3, "dora");
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BootElastic.class)
#WebAppConfiguration
public class CacheTest {
#Autowired
private CacheObject cacheObject;
#Autowired
private CacheReference cacheReference;
#Test
public void testCache(){
String name = "this is neal for cache test";
long userId = 1;
cacheObject.setName(userId, name);
// cacheObject.setName(2, "neal"); // this will make test success
String nameFromCache = cacheReference.getNameOut(userId);
System.out.println("1" + nameFromCache);
Assert.assertEquals(nameFromCache, name);
}
#Test
public void testCacheFromConfig(){
String nameFromCache = cacheReference.getNameOut(2);
System.out.println("4" + nameFromCache);
Assert.assertEquals(nameFromCache, "neal");
}
}
#PostConstruct methods are called right after all postProcessBeforeInitialization() BeanPostProcessor methods invoked, and right before postProcessAfterInitialization() invoked. So it is called before there is any proxy around bean, including one, putting values to cache.
The same reason why you can't use #Transactional or #Async methods in #PostConstruct.
You may call it from some #EventListener on ContextRefreshedEvent to get it working
It seems that the setter on my bean is not working.
This is my Spring java configuration, SpringConfig.java:
#Configuration
#ComponentScan("com.xxxx.xxxxx")
public class SpringConfig {
#Bean(name="VCWebserviceClient")
public VCWebserviceClient VCWebserviceClient() {
VCWebserviceClient vCWebserviceClient = new VCWebserviceClient();
vCWebserviceClient.setSoapServerUrl("http://localhost:8080/webservice/soap/schedule");
return vCWebserviceClient;
}
The VCWebserviceClient.java:
#Component
public class VCWebserviceClient implements VCRemoteInterface {
private String soapServerUrl;
public String getSoapServerUrl() {
return soapServerUrl;
}
public void setSoapServerUrl(String soapServerUrl) {
this.soapServerUrl = soapServerUrl;
}
// Implemented methods...
}
My app.java:
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
VCWebserviceClient obj = (VCWebserviceClient) context.getBean("VCWebserviceClient");
System.out.println("String: "+obj.getSoapServerUrl()); // returns NULL
Why is obj.getSoapServerUrl() returning NULL?
This example shows how it should work.
The instance returned by VCWebserviceClient is not the one actually used by your application. It is a way for Spring to know what class to instanciate.
Any way, for you issue, use the #Value (http://docs.spring.io/spring/docs/3.0.x/reference/expressions.html)
#Component
public class VCWebserviceClient implements VCRemoteInterface {
// spring resolves the property and inject the result
#Value("'http://localhost:8080/webservice/soap/schedule'")
private String soapServerUrl;
// spring automatically finds the implementation and injects it
#Autowired
private MyBusinessBean myBean;
public String getSoapServerUrl() {
return soapServerUrl;
}
public void setSoapServerUrl(String soapServerUrl) {
this.soapServerUrl = soapServerUrl;
}
// Implemented methods...
}