I've created a OAuth2 Resource Server that accepts and validates JWT tokens and extracts user information from the claims in order to determine user information such as username and authorities. I've largely done this using spring-security-oauth2-autoconfigure library.
After a user is authenticated, I'd like to call custom code that puts a message on a Kafka stream to indicate that a user has logged in. Where is the most appropriate place to do this?
I could do this in OAuth2AuthenticationManager.authenticate, but I'd have to extend that class and override that method, and then wire it in. It seems like Spring should have something already in place to handle this.
OAuth2AuthenticationProcessingFilter, which calls the authenticate method mentioned in the question, has a member called eventPublisher. Its methods include publishAuthenticationSuccess, which is called following a successful authentication.
To tie custom code in to this, create an event listener that gets picked up as a bean by Spring. Something like this:
#Component
public class MyAuthenticationEventListener implements ApplicationListener<AuthenticationSuccessEvent> {
private static final Logger logger = LoggerFactory.getLogger(MyAuthenticationEventListener.class);
#Override
public void onApplicationEvent(AuthenticationSuccessEvent authenticationSuccessEvent) {
logger.info("User logged in: " + authenticationSuccessEvent.getAuthentication().getName());
}
}
Related
I'm trying to get logged in users' details who are connected via websocket on Spring Boot 2. To do this, I'm currently using SimpUserRegistry to find a list of connected SimpUsers.
Since Spring Boot 2.4, I noticed the SimpUser class has a getPrincipal() method that returns a generic Principal object. As each user is supposed to login via Spring Security's mechanisms, I thought I was able to cast it to Spring Security's UserDetails to get the logged in user , but I realize it wasn't the case.
Does anyone know how I can make use of getPrincipal or other ways to get logged in userDetails?
First of all, let's make it clear that, to use getPrincipal() with websocket, you have to implement websocket authentication and authorization through Interceptor (as far as I know SpringSecurity doesn't do this automatically).
After doing the above correctly, you can now use the getPrincipal () method. It will return The identity of the principal being authenticated (maybe Username, email,...)
You can use code that looks like this:
#MessageMapping("/test")
public void doSomething(#Payload AppMessage appMessage, Principal principal) {
String username = principal.getName();
// find userDetail with username here
}
Working with Spring / Spring security on a small project at the moment and having difficulty implementing this feature. Ideally I only want user1 to view the details of user1 and not those of 2,3 or 4 for example.
I've implemented Spring Security with Roles and understand that I can retrieve a UserDetails object or a principle, I'm not sure exactly but I know I can retrieve the details of the current logged in user using one of, what appears to be many methods.
This is what I'm currently using as a proof of concept when we go to the Admin/home page:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Gamer gamer = gamerService.findGamerByEmail(auth.getName());
System.out.println("Auth: " + auth.getName());
System.out.println("Gamer: " + gamer.getName() + gamer.getId() + gamer.getEmail());
The security config takes care of whether or not the current user can access because of the roles assigned to it.
I believe I should be able to go to the url of /mysite/viewUserDetails and have that page display information of the current user but I cannot find any examples of this, I've found plenty of example that prove a logged in user can view a page but none that specify checks in place to ensure user1 can only view user1's details.
On an older page I do this to display information for a particular user but I understand this to be bad practice-
<a th:href="#{/gamer/{gamerid}/games/excited (gamerid=${gamer.id}) }">
*Worth noting that this isn't using any form of login/registration to pull out this info, I'm simple using the id I pass in as part of the DB query.
It maps onto :
#RequestMapping("/gamer/{gamerid}/games/excited")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
So my question becomes, and I hope you can point in the right direction, How can I implement a solution where a user can only view his/her details and how should this be represented via the form and connecting controllers as passing ids in the url is kinda ugly (I could use a guid but...)
Thanks so much in advance.
It's actually quite an easy choice. Either you have an entry point like:
#RequestMapping("/gamer/{gamerid}/games/excited")
and you manually check that the user in session can access the requested resource, or you have something like
#RequestMapping("/my-games")
that automatically reads the user id from the security context.
More than a security choice, I'd pick one depending on code reuse and future use-cases (for example the same page/set of pages can be seen by more than one user).
Have a look at #PreAuthorize annotation. It is possbile to annotate given endpoint with it and create custom logic in a bean. Then you can use custom method to allow or disallow the endpoint to proceed :
#Controller
public class HomeController {
#Autowired
private AuthenticationService authenticationService;
#RequestMapping("/gamer/{gamerid}/games/excited")
#PreAuthorize("#authenticationService.hasAccess(#gamerid)")
public String getExcited(#PathVariable final Long gamerid, Model model){
addGamerListAttributes(model, gamerid, "EXC");
return "games";
}
}
Service class :
#Service
public class AuthenticationService {
public boolean hasAccess(String tgamerid) {
//implement logic here
return true;
}
}
Method hasAccess in the AuthenticationService should return boolean. #PreAuthorize will be launched before controller handler method is invoked. The controller above is just an example. You can pass Authentication object in SPeL expression in #PreAuthorize annotation to service method or get it from security context inside service class, to implement logic which fits your needs. More information can be found here and in Spring Docs.
I have some JSF 2 applications that are currently working with a JSF Secutiry LoginModule (auth-method = FORM). But authentication will be done now through a new way, that means I'll have to manually program the authentication interactions.
That's fine, but now I have problems setting the roles. I couldn't find where I can set the Principals, or get subject to do it, or get shared state to put "javax.security.auth.principal" and "javax.security.auth.roles" variables.
Is there a way to do it? Here is a sample of my actual Bean code.
Thanks in advance!
#ManagedBean
#ViewScoped
public class PrincipalController extends AbstractController implements ExcluirRascunhoService.Presenter {
// has get and set
#ManagedProperty(value = "#{autenticacaoController}")
private AutenticacaoController autenticacaoController;
#PostConstruct
private void init() {
try {
// a previous application redirected the user here,
// giving two parameters, including a valid and calculated HASH
// to be passed to authentication
Map<String, String> requestMap = getContext().getRequestParameterMap();
String user = (String) requestMap.get("login");
String hash = (String) requestMap.get("hash");
// this will do the authentication, communicating with a
// webservice and passing these data so the webservice can
// authenticate the data, telling me if the user is Ok
autenticacaoController.authenticate(user, hash);
// do the other things if authentication doesn't throw an exception
// I should now fill all user's Roles accordingly to my database
// I get them correctly, but how to set them into the JSF Roles?
} catch (AuthenticationException e) {
// catch and quit the page
}
}
}
You can't do it with JSF alone. Basically, JSF only provide a utility to get the user and it's role directly from the JSF interface. So, if you want to access the principal user and it's roles from your application you must first authenticate the user.
To authenticate your user you can use an third party solution like JAAS or Apache Shiro to setup the realm, roles and control the authentications of your application.
You can also roll your own authentication layer, which may not give you the possibility to use some useful JSF utilities like getting the principal user or it's roles directly from the realm (note that your custom layer will probably provide other ways to get these values), but will also provide a custom way to do the authentication required.
I have found a pretty nice tutorial about JAAS authentication layer (in portuguese) that may help you setting up a authentication layer.
Wish you good luck and feel free to ask if you have any doubts about what I've said.
Consider an Struts 2 + Spring 4 project.
For each login the User object is put in session. As a very simple action it will look
public class LoginProcess implements ServletRequestAware {
#Inject
private AuthenticationServices authenticationServices;
public String execute() {
//The login method makes a new User and fills its setters
User newUser = authenticationServices.login(....);
getServletRequest().getSession().setAttribute("USER_SESSION", user);
}
}
As we manually make a new User object so it is not managed spring bean, and we can't use spring features in User class: #Inject , #Value ,...
I tried to change user as:
#Named
#Scope(value="session")
public class User { ...
#Inject
private AccountServices accountServices;
}
and inject the User in instead of calling new User, but I get the error:
Caused by: java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:91)
Well although it describes the error, but I can not find how can I fix it, and I am not sure if this is the correct way at all. It seems that I can only use spring session scope been when I am using spring mvc
Any comments ?!
Why I need this ?! (Simplified situation)
The user object has a getAccounts() methods which get all user accounts. Getting user accounts is an expensive operation, and it is possible that a user does not require its accounts during its login.
So, instead of get user accounts as soon as user logs in, we let the get method get user accounts if it does not have it:
public class User() {
private Accounts accounts;
#Inject
private AccountServices accountServices;
Accounts getAccounts() {
if (accounts == null) {
accounts = accountServices.getUserAccountsFromDB(...)
}
return accounts;
}
Don't create a new instance of User by yourself, instead get a bean from Spring context.
For example you can achieve it by implementing ApplicationContextAware interface and calling one of getBean methods.
User user = applicationContext.getBean(User.class);
// populate user and put it into session
In that way it is a Spring managed bean an all required properties should be injected.
BUT consider changing your User to a simple POJO and moving all business logic (such as fetching users accounts) to some more appropriate place, in that way your model layer will be cleaner and easily testable.
I have a Spring web application which uses Spring SAML and Spring Security to manage the login process.
Now I need to do some tasks after the correct login occurs. In particular I have to store some data in the SecurityContext.getContext() object.
I have never worked with Spring Security/SAML and I don't know how it manages the return from the IdP.
Is there any place in the code where usually you can put your code after the login process ends correctly?
I mean, I know where the redirect page is set but I cannot put my custom code in the Controller of this redirect page because that page is accessed more than one time, and I need to run my custom code only once at login time.
The best approach is to implement interface SAMLUserDetailsService, which will automatically store object you return from its loadUserBySAML method in the Authentication object which you can later query from the SecurityContext.getContext(). The interface is called once after each authentication. See the manual for details and examples.
The other possibility is AuthenticationSuccessHandler. The login process calls method onAuthenticationSuccess which has access to the Authentication object, which will be stored in the SecurityContext.getContext().
Simply create your own class which implements interface AuthenticationSuccessHandler (you can also extend some of the existing classes, such as SimpleUrlAuthenticationSuccessHandler or AbstractAuthenticationTargetUrlRequestHandler). Then plug your implementation to the securityContext.xml by changing class in the existing successRedirectHandler bean.
The problem is, that the Authentication object tends to be immutable - so the first way might be better.
You can use AuthenticationSuccessEvent. Just register a bean that implements ApplicationListener.
#Component
public class SomeSpringBean implements
ApplicationListener<AuthenticationSuccessEvent> {
public onApplicationEvent(AuthenticationSuccessEvent event) {
String userName = ((UserDetails) event.getAuthentication().
//do stuff
}
}
And you need to register AuthenticationEventPublisher.
Take a look here: https://gist.github.com/msarhan/10834401
If you use custom authentication provider, you can also plug whatever you want there.
Are you using Spring's Java configs?
If so, then you probably have a class that overrides WebSecurityConfigurerAdapter in your project. Extending this class gives you access to override the method configure(HttpSecurity http).
You can use that provided HttpSecurity builder object to configure a lot of things, one of which is the authentication success handler. More or less, you can create a simple that class that implements AuthenticationSuccessHandler (Spring has a few classes already built for extension to make this easy), and you can call http.successHandler(yourSuccessHandler) to register it with Spring Security.
Implementing that interface gives you the hook to put custom code into the onAuthenticationSuccess( ... ) method. I think they have one for failures as well.