SecurityContextHolder.getContext().getAuthentication() always return 'anonymousUser' - spring

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);
}
}

Related

Spring Security - Process custom annotation in GenericFilterBean

in my controller I have a custom annotation like:
#GetMapping("/apikey")
#Secured(apiKeys = { ApiKey.APP_1}) // <- Custom annotation
public ResponseEntity startApiKey() {
return ResponseEntity.status(HttpStatus.OK).body("ApiKey approved");
}
In my Spring Security Config I have added a Filter for checking the apikey and authentication:
public class ApiKeyAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(ApiKeyHeadername.DEFAULTHEADERNAME.getHeadername());
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
#Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authentication -> {
String principal = (String) authentication.getPrincipal();
if (!ApiKey.APP_1.getApiKey().equals(principal))
{
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
});
}
}
Before the custom annotation was proccessed within a AspectJ class:
#Component
#Aspect
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SecurityAspect {
#Autowired
private IPrincipal principal;
#Autowired
private AuthorizationManager authorizationManager;
#Pointcut("#annotation(my.demo.application.security.aspect.Secured)")
public void methodAnnotatedWithSecured() {
}
#Around("methodAnnotatedWithSecured()")
public Object userAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Secured securedAnnotation = method.getAnnotation(Secured.class);
Authorized securityInformation = new Authorized(securedAnnotation.apiKeys(), securedAnnotation.roles(),
securedAnnotation.usernames());
if (authorizationManager.authorizeUserPrincipal(principal,
securityInformation) == AuthorizationState.UNAUTHORIZED) {
throw DefaultNotAuthorizedExceptionFactory.createNotAuthorizedException();
}
return joinPoint.proceed();
}
}
How can I process the annotation informations in the AbstractPreAuthenticatedProcessingFilter, or how can i get the annotation by Reflection in this Filter. Or can I inject something to get it?
Thank you in advice

Spring boot - Pass argument from interceptor to method in controller

For learning purposes, I have made a custom authentication system where I pass a token from the client to the server through the Authorization header.
In the server side, I'd like to know if it's possible to create in the interceptor, before the request reaches a method in the controller, an User object with the email from the token as a property, and then pass this user object to every request where I require it.
This what I'd like to get, as an example:
#RestController
public class HelloController {
#RequestMapping("/")
public String index(final User user) {
return user.getEmail();
}
}
public class User {
private String email;
}
Where user is an object that I created in the pre-interceptor using the request Authorization header and then I can pass, or not, to any method in the RestController.
Is this possible?
#Recommended solution
I would create a #Bean with #Scope request which would hold the user and then put the appropriate entity into that holder and then take from that holder inside the method.
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class CurrentUser {
private User currentUser;
public User getCurrentUser() {
return currentUser;
}
public void setCurrentUser(User currentUser) {
this.currentUser = currentUser;
}
}
and then
#Component
public class MyInterceptor implements HandlerInterceptor {
private CurrentUser currentUser;
#Autowired
MyInterceptor(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
this.currentUser.setCurrentUser(new User("whatever"));
return true;
}
}
and in the Controller
#RestController
public class HelloController {
private CurrentUser currentUser;
#Autowired
HelloController(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#RequestMapping("/")
public String index() {
return currentUser.getCurrentUser().getEmail();
}
}
#Alternative solution
In case your object that you would like to have, only contains one field, you can just cheat on that and add that field to the HttpServletRequest parameters and just see the magic happen.
#Component
public class MyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//TRY ONE AT THE TIME: email OR user
//BOTH SHOULD WORK BUT SEPARATELY OF COURSE
request.setAttribute("email", "login#domain.com");
request.setAttribute("user", new User("login#domain.com"));
return true;
}
}
You can use a local thread context object as follows - which will be handling one parameter per request thread (thread safe):
public abstract class LoggedUserContext {
private static ThreadLocal<User> currentLoggedUser = new ThreadLocal<>();
public static void setCurrentLoggedUser(User loggedUser) {
if (currentLoggedUser == null) {
currentLoggedUser = new ThreadLocal<>();
}
currentLoggedUser.set(loggedUser);
}
public static User getCurrentLoggedUser() {
return currentLoggedUser != null ? currentLoggedUser.get() : null;
}
public static void clear() {
if (currentLoggedUser != null) {
currentLoggedUser.remove();
}
}
}
Then in the interceptor prehandle function:
LoggedUserContext.setCurrentLoggedUser(loggedUser);
And in the interceptor postHandler function:
LoggedUserContext.clear();
From any other place:
User loggedUser = LoggedUserContext.getCurrentLoggedUser();

how to use session scoped bean in jhipster microservice architecture

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

Post authorizing Spring asynchronous controller response

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;
}
}

Spring social returning wrong user profile

I'm using Spring Social LinkedIn to retrieve user profiles with a custom ConnectController since I want to the user to login and retrieve the profile in one step. The issue is that sometimes the first user in the system is returned instead of the currently logged in user.
Here is my CustomConnectController
#Controller
#RequestMapping("/connect")
public class CustomConnectController extends ConnectController {
#Inject
public CustomConnectController(ConnectionFactoryLocator connectionFactoryLocator,
ConnectionRepository connectionRepository) {
super(connectionFactoryLocator, connectionRepository);
}
#Override
protected String connectView(String providerId) {
return "redirect:/hey/" + providerId + "Connect";
}
#Override
protected String connectedView(String providerId) {
return "redirect:/hey/" + providerId + "Connected";
}
}
and my webcontroller
#Controller
public class WebController {
#Autowired
private LinkedIn linkedin;
#Autowired
private ConnectionRepository repository;
#RequestMapping(value = "/hey/linkedinConnected", method = RequestMethod.GET)
public String linkedinConnected(HttpServletRequest request, Model model, Locale locale) {
if (repository.findConnections("linkedin").isEmpty()
|| !linkedin.isAuthorized()) {
return "redirect:/connect/linkedin";
}
LinkedInProfile userProfile = linkedin.profileOperations().getUserProfile();
return "loggedinpage";
}
#RequestMapping(value = "/hey/linkedinConnect", method = RequestMethod.GET)
public String linkedinConnect(HttpServletRequest request, Model model, Locale locale) {
if (repository.findConnections("linkedin").isEmpty()
|| !linkedin.isAuthorized()) {
return "redirect:/connect/linkedin";
}
LinkedInProfile userProfile = linkedin.profileOperations().getUserProfile();
return "loggedinpage";
}
}
Any ideas of what I'm doing wrong?

Resources