I have a Spring MVC application where I'm exposing an endpoint, and a small library where I wrote some common functionality.
I have an utility class like this:
class SecurityUtil {
public static Principal getPrincipal(){
return SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();
}
}
And from the Controller I'm doing something like:
class MyController {
public ResponseEntity<Void> myEndpoint(){
// do something
Principal principal = SecurityUtil.getPrincipal();
// use the principal information for some audit processes
}
}
In this case the Principal is null, but if replace my code like this:
class MyController {
public ResponseEntity<Void> myEndpoint(){
// do something
Principal principal = SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
// use the principal information for some audit processes
}
}
In this case the Principal is not null and it has the information that I need.
Do you know what could be happening?
I was going through the same problem and then I have solved it in following manner.
Create UserService interface
public interface UserService {
String getLoggedInUserName();
User getLoggedInUser();
}
Provide an implementation for UserService, However, you can also it without creating the interface and by simply creating UserService as a class.
#Service
public class UserServiceImpl implements UserService {
private static Log log = LogFactory.getLog(UserServiceImpl.class);
#Override
public String getLoggedInUserName() {
try {
return getLoggedInUser().getUsername();
} catch (Exception ex) {
throw new UsernameNotFoundException("Please Log in", ex);
}
}
#Override
public User getLoggedInUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.getPrincipal() instanceof User) {
return (User) authentication.getPrincipal();
} else {
throw new UsernameNotFoundException("User is not authenticated; Found " + authentication.getPrincipal() + " of type " + authentication.getPrincipal().getClass() + "; Expected type User");
}
}
}
And the calling userService.getLoggedInUserName() by auto wiring UserService
#Autowired UserService userService
Update:
If you are getting them in your controller only then you can simply pass Principal principal as a method argument to your controller method instead of getting it from the security context. It will be auto-wired to controller automatically and later on you can pass it your service methods. This way is also considered a good practice Spring MVC, getting principal from security context in service layer
#RequestMapping(value = "/myEndpoint", method = GET)
public ResponseEntity<Void> myEndpoint(Principal principal){
// do something
// use the principal information for some audit processes
}
Related
This is a typical Spring controller method.
public ResponseEntity<RestApiResponse<OTPResponse>> sendOtp(HttpServletRequest request) {
UserDetails userDetails = (UserDetails) request.getSession().getAttribute(SessionKey.USER_DETAILS);
// Do some work
//...
}
To get the username I have to copy the bellow line over and over again in every other controller method.
UserDetails userDetails = (UserDetails) request.getSession().getAttribute(SessionKey.USER_DETAILS);
Can I do this as follows ?
// #UserDetails is some kind of imaginary annotation
public ResponseEntity<RestApiResponse<OTPResponse>> sendOtp(#UserDetails UserDetails userDetails) {
userDetails.getUsername();
// Do some work
//....
}
Can I intercept the request, get the userDetails from request and inject that as controller method argument?
you can use below code as util method
public UserDetails getUser(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
return (UserDetails) auth.getPrincipal();
}
return null;
}
Another way of doing same.
#RequestMapping(method = RequestMethod.GET)
public ModelAndView anyMethodNameGoesHere(Principal principal) {
final String loggedInUserName = principal.getName();
}
You can intercept requests and do it yourself, however spring-security already have such feature. It is called #AuthenticationPrincipal.
You can use it as follow:
#GetMapping
String hello(#AuthenticationPrincipal java.security.Principal principal) {
return principal.getName();
}
If having a Principal isn't enough for your usecase, User also works:
#GetMapping
String hello(#AuthenticationPrincipal org.springframework.security.core.userdetails.User user) {
return user.getUsername();
}
You can even inject your custom user details this way if you want.
Please have a look at documentation.
I have created a asp.net web api project and implemented the below HTTP GET method in AccountController and the related service method & repository method in AccountService & AccountRepository respectively.
// WEB API
public class AccountController : ApiController
{
private readonly IAccountService _accountService;
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
[HttpGet, ActionName("UserProfile")]
public JsonResult<decimal> GetUserSalary(int userID)
{
var account = _accountService.GetUserSalary(userID);
if (account != null)
{
return Json(account.Salary);
}
return Json(0);
}
}
Service / Business Layer
public interface IAccountService
{
decimal GetUserSalary(int userId);
}
public class AccountService : IAccountService
{
readonly IAccountRepository _accountRepository = new AccountRepository();
public decimal GetUserSalary(int userId)
{
return _accountRepository.GetUserSalary(userId);
}
}
Repository / Data Access Layer
public interface IAccountRepository
{
decimal GetUserSalary(int userId);
}
public class AccountRepository : IAccountRepository
{
public decimal GetUserSalary(int userId)
{
using (var db = new AccountEntities())
{
var account = (from b in db.UserAccounts where b.UserID == userId select b).FirstOrDefault();
if (account != null)
{
return account.Salary;
}
}
return 0;
}
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IAccountService, AccountService>();
container.RegisterType<IAccountRepository, AccountRepository>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
But when I invoke the API method GetUserSalary() I get an error saying
An error occurred when trying to create a controller of type 'AccountController'. Make sure that the controller has a parameterless public constructor.
Check that you did not forget to register Unity IoC container itself:
if you use ASP.NET Framework it could be - Global.asax or Startap.cs (Owin) via UnityConfig.RegisterComponents() method.
if you use ASP.NET Core then in the Startup.cs file (I was unable to find official guides for its configuting)
Your current constructor has parameters (or args if you prefer).
see:
public AccountController(IAccountService accountService)
{
_accountService = accountService;
}
All you need to do is add a "Parameter-less Constructor" into the controller as well.
public AccountController()
{
}
Parameter-less constructors are usually above the ones that have params, though as far as I am aware this is only due to standards not any actual effect(s) it may cause.
There is also an already existing issue/question similar to this I will link below that may provide further details.
Make sure that the controller has a parameterless public constructor error
I succesfully added user_id additionnal information on the generated tokens on the authorization server side by implementing a TokenEnhancer. Here is a token generated:
{"access_token":"ccae1713-00d4-49c2-adbf-e699c525d53e","token_type":"bearer","expires_in":31512,"scope":"end-user","user_id":2}
Now, on the Resource server side, which is a completely separate spring project communicating through a RemoteTokenServices, i would like to use theses informations with method expression-based access control. For example i would like to use the added user_id data (it is Spring Data JPA repository for use with Spring Data Rest):
#PreAuthorize("#oauth2.hasScope('admin') or #id == authentication.principal.user_id")
#Override
UserAccount findOne (#P("id") Integer id);
The #oauth2.hasScope('admin') works as expected but the #id == authentication.principal.user_id" part obviously not.
how can i access to the additional data added to the token on expression-based access control ?
So i've found myself. The key interface is UserAuthenticationConverter.
Using the default provided DefaultUserAuthenticationConverter class, we can set a UserDetailsService which is used to set authentication.principal with the UserDetail object returned by the UserDetailsService. Without that, authentication.principal is only set with the token username as a String.
Here is an extract of my ResourceServerConfigAdapter:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration
extends ResourceServerConfigurerAdapter {
#Bean
UserDetailsService userDetailsService () {
return new UserDetailsServiceImpl();
}
#Bean
public UserAuthenticationConverter userAuthenticationConverter () {
DefaultUserAuthenticationConverter duac
= new DefaultUserAuthenticationConverter();
duac.setUserDetailsService(userDetailsService());
return duac;
}
#Bean
public AccessTokenConverter accessTokenConverter() {
DefaultAccessTokenConverter datc
= new DefaultAccessTokenConverter();
datc.setUserTokenConverter(userAuthenticationConverter());
return datc;
}
#Bean
RemoteTokenServices getRemoteTokenServices () {
RemoteTokenServices rts = new RemoteTokenServices();
rts.setCheckTokenEndpointUrl(
"http://localhost:15574/oauth/check_token");
rts.setAccessTokenConverter(accessTokenConverter());
rts.setClientId("client");
rts.setClientSecret("pass");
return rts;
}
...
}
Another method is to override the DefaultUserAuthenticationManager and provide a custom public Authentication extractAuthentication(Map<String, ?> map).
Once this is done, we can use the user data on expression-based access control like that:
#PreAuthorize("#oauth2.hasScope('admin') or #id == authentication.principal.userAccount.id")
#Override
UserAccount findOne (#P("id") Integer id);
Note that userAccount is my original DOMAIN user object. It could be everything the UserDetailsService returns.
EDIT:
To answer to Valentin Despa, here is my UserDetailsService implementation:
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserAccountRepository userAccountRepository;
public UserDetails loadUserByUsername (String username)
throws UsernameNotFoundException {
// Fetch user from repository
UserAccount ua = this.userAccountRepository
.findByEmail(username);
// If nothing throws Exception
if (ua == null) {
throw new UsernameNotFoundException(
"No user found having this username");
}
// Convert it to a UserDetails object
return new UserDetailsImpl(ua);
}
}
I want to access the logged-in user from a session-scoped spring bean, is that possible?
I'm not using spring security, but openam instead to provide security to my web application, so I can't use this (as I've seen in many examples on the internet):
(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Is it possible to inject into my session-scoped bean the same name that you get from:
HttpServletRequest.getUserPrincipal().getName()
You can try creating an Interceptor and setting the logged in user to a property of your session bean, which can be injected into your interceptor.
Like this:
public class SessionDataInitializerInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOG = Logger.getLogger(SessionDataInitializerInterceptor.class);
#Autowired
SessionData sessionData;
public SessionDataInitializerInterceptor() {
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Principal principal = request.getUserPrincipal();
if (principal != null) {
if (sessionData.getUser() == null) {
sessionData.setUser(principal.getName());
}
} else {
LOG.error(String.format("No user principal in request for url[%s]", request.getRequestURL().toString()));
}
return true;
}
}
Don't forget to map your interceptor to the appropriate URLs.
Also this works:
#Component
#SessionScope
public class SessionData {
#Autowired
private HttpServletRequest request;
#PostConstruct
public void init() {
Principal principal = request.getUserPrincipal();
}
}
I have two questions regarding two annotations:
1) Why does the "faceContext" has to be injected from Resources class? Instead, MemberController can directly use "FacesContext.getCurrentInstance()" in register() method to obatin a FacesContext object? It seems much simpler to do that.
2) Can #Model be replaced by #Singleton? Or even #ApplicationScoped?
Thanks.
MemberController.java
#Model
public class MemberController {
#Inject
private FacesContext facesContext;
#Inject
private MemberRegistration memberRegistration;
#Produces
#Named
private Member newMember;
#PostConstruct
public void initNewMember() {
newMember = new Member();
}
public void register() throws Exception {
try {
memberRegistration.register(newMember);
FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_INFO, "Registered!", "Registration successful");
facesContext.addMessage(null, m);
initNewMember();
} catch (Exception e) {
String errorMessage = getRootErrorMessage(e);
FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_ERROR, errorMessage, "Registration unsuccessful");
facesContext.addMessage(null, m);
}
}
}
Resources.java
public class Resources {
// use #SuppressWarnings to tell IDE to ignore warnings about field not being referenced directly
#SuppressWarnings("unused")
#Produces
#PersistenceContext
private EntityManager em;
#Produces
public Logger produceLog(InjectionPoint injectionPoint) {
return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
#Produces
#RequestScoped
public FacesContext produceFacesContext() {
return FacesContext.getCurrentInstance();
}
}
injecting the FacesContext istead of getting it using the static factory method has the advantage that you will only once have to care about how to get the current context, when implementing the producer method or field. Each time you need the context you can simply inject it and it is fully transparent to you where it comes from. This might also have some benefits when anything changes in how to get the context, ...
The answer to the second question depends on your requirements. Since #Model is simply a stereotype for #RequestScoped and #Named, you cannot directly replace it with #Singleton or #ApplicationScoped since these both annotations adwise the container to create a single object for all requests. Nevertheless, if this meets your requirements better than having a different object for each request, you are free to change it ;)