Reputation: 767
I have a bean which I've declared in my bean config as thus:
@Configuration
public class BeanConfig {
@Bean
public MemberDTO getMemberDTO() {
return new MemberDTO();
}
}
When a user calls my service, I use the username and password they've provided to call the endpoint of a different service to get the user's information:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOGGER = LogManager.getLogger(CustomAuthenticationProvider.class);
private @Autowired MemberDTO memberDTO;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String loginGeniuneFailMessage = "";
boolean loginGeniuneFail = false;
try {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
String endPoint = credentialsBaseUrl + "/api/login";
HttpResponse<MemberDTO> response_auth = Unirest.get(endPoint)
.basicAuth(username, password)
.header("Accept", "*/*")
.asObject(MemberDTO.class);
int status_auth = response_auth.getStatus();
if (status_auth == 200) {
if (response_auth.getBody() == null) {
LOGGER.info("account validation - could not parse response body to object");
UnirestParsingException ex = response_auth.getParsingError().get();
LOGGER.error("parsing error: ", ex);
} else {
memberDTO = response_auth.getBody();
}
}
...
} catch (Exception ex) {
...
}
}
I want to store the user's information in the memberDTO and use the memberDTO elsewhere in a different component, rather than calling the login API every time:
@Component
public class MemberLogic {
private @Autowired MemberDTO memberDTO;
public ResponseEntity<?> processMemberInformation(WrapperDTO wrapperDTO, BindingResult result) {
if (result.hasFieldErrors()) {
String errors = result.getFieldErrors().stream()
.map(p -> p.getDefaultMessage()).collect(Collectors.joining("\n"));
return ResponseEntity.badRequest().body("An error occured while trying to persist information: " + errors);
}
String name = memberDTO.getName();
...
}
}
The problem now is the "memberDTO.getName()" is returning null, even though this value is being set from the initial API call in CustomAuthenticationProvider.
My questions are: why isn't this working? And is this the best approach to take for something like this?
Thanks.
Upvotes: 0
Views: 2965
Reputation: 2085
The problem is, that you can not override a spring bean "content" like this memberDTO = response_auth.getBody();
because it changes only the instance variable for the given bean. (And its also not good because its out of the spring boot context and it overrides only the field dependency for this singleton bean)
You should not use a normal spring bean for holding data (a state). All the spring beans are singleton by default and you could have some concurrency problems.
For this you should use a database, where you write your data or something like a session bean.
Upvotes: 1
Reputation: 15212
My questions are: why isn't this working? And is this the best approach to take for something like this?
This doesn't work because Java uses pass-by-value semantics instead of pass-by-reference semantics. What this means is that the statement memberDTO = response_auth.getBody();
does not really make the Spring container start pointing to the MemberDTO
returned by response_auth.getBody()
. It only makes the memberDTO
reference in CustomAuthenticationProvider
point to the object in the response. The Spring container still continues to refer to the original MemberDTO
object.
One way to fix this would be to define a DAO
class that can be used for interacting with DTO
instances rather than directly creating a DTO
bean :
@Configuration
public class BeanConfig {
@Bean
public MemberDAO getMemberDAO() {
return new MemberDAO();
}
}
CustomAuthenticationProvider
can then set the MemberDTO
in the MemberDAO
by using : memberDAO.setMemberDTO(response_auth.getBody());
Finally, MemberLogic
can access the MemberDTO
as String name = memberDAO.getMemberDTO().getName();
Note : Instead of returning the MemberDTO
from the MemberDAO
, the MemberDAO
can define a method called getName
which extracts the name from the MemberDTO
and returns it. (Tell Don't Ask principle). That said and as suggested in the comments, the best practice would be to use a SecurityContext
to store the user information.
Upvotes: 1