I am using hiberbate 3.5 and spring 3.x
I have envers working and the ... _aud and revinfo record are now being written.
I now need to add the username to the audit record I'm guessing revinfo is the best place I have seen an example for seam app but nothing for spring app in jboss
Can anyone help with this please.
My main aim is to be able to record the windows authenticated user.
The Envers documentation has an answer for this if you use Seam.
public class ExampleListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
Identity identity = (Identity) Component.getInstance("org.jboss.seam.security.identity");
exampleRevEntity.setUsername(identity.getUsername());
}
}
So, I suppose your question would be how to retrieve the currently logged in user at that point if you don't use Seam? I'm sure there are several ways to do it, we do it like this.
Write a ServletFilter
In the filter get the principal from the session (or the security context if you use a security framework like Spring Security)
Store it in the log4j MDC
Code example from filter
protected void beforeRequest(final HttpServletRequest request, final String message) {
final Principal principal = request.getUserPrincipal();
final String username = principal != null ? principal.getName() : null;
if (username != null) {
MDC.put(USER, username);
}
}
protected void afterRequest(final HttpServletRequest request, final String message) {
MDC.remove(USER);
}
Later you can get the user from anywhere in your code because the MDC has a static get(String) method.
Related
I was exploring spring security and tried to build a small application wherein I have a an entity name User and a userRepository having a one declared method findByUserName(String userName)
#Entity
#Table(name="user")
class User {
#id
private Long id;
private String userName;
private String password;
}
I have heard that spring security depends on principles and not users.
So we have to have a class which implements UserDetails (provided by spring security).
What's the reason behind this?
Secondly, once we have written all this code we need to configure it into a class which I have done as shown below:
public class AppSecurityConfid extends WebSecurityCongigurerAdapter {
// here we have to autowire the service class which we have made to call the
userRepository and find the user based on userName
#Bean
public DAOAuthenicationProvider authenicationProvider() {
// wherein we create an instance and pass the autowired instance and set the
password encoder and return the instance
}
protected void configurer(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
}
Things up to here make sense, but why we need Authentication Build Manager in this scheme of things?
I am not an expert but I'd like to say something, maybe it can help:
Spring uses "in the background" a way to retrieve user data for authentication when you activate Spring Security. Of course, this method can be overriden so the developer can change how Spring obtains this data in order to support situations where the data is sparced in different tables, from a file, an API REST query, etc.
The authentication data is structured as a list, where each element corresponds to the data used to authenticate each user. This data is structured as a tuple of 3 elements: String username, String hashedPassword and boolean isAccountActive.
You need to provide a way to obtain this data for each user. You do not need to provide the data explicitly, just the way (method) to obtain it. One way to do it (as you said) is creating a class that implements UserDetailsService, which, for example, forces you to implement UserDetails loadUserByUsername(String email);. In this method you need to provide an instance of a class that implements UserDetails, which corresponds to the UserDetails of the User with the username passed as a parameter. This methods (and similar) are used by Spring Security "in the background" to retrieve the UserDetails of a certain user when is trying to access your web server.
If the Userdetails match with the credentials provided in the request, Spring will allow the request to hit your controllers; else it will throw a HTTP 401. Of course there are other authentication methods, but for the sake of simplicity we understand credentials as user / password with HTTP basic authentication.
So, why do we need a class that implements UserDetails? Because is the contract that a class needs to fulfill if it has to be used for internal authentication in Spring Security. Also to separate from a User class the logic of the business with the logic of the security. Probably creating your own class that extends UserDetails is the best idea, but is not actually necessary. For example if you have your own class to contain the data of a user, you just need to understand how to transform your User instance to UserDetails, so Spring Security can use it transparently and the opposite: how the UserDetails instance can be transformed into one of your users.
For example this is a method to obtain the User instance using the UserDetails instance that is currently authenticated in Spring Boot.
#Service
public class SecurityServiceClass{
#Override
public User getLoggedUser() {
String username = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername();
Optional<User> user = this.userService.get().stream().filter(r -> r.getEmail().equals(username)).findFirst();
UserDetails userDetails = ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
// TODO: make error in case of null
return user.orElse(new User());
}
}
Here I retrieve the User by retrieving the username from the UserDetails and querying it to the DB to recover the User. I am accessing the DB using a repository class.
Here I do the opposite, transforming a User to a UserDetails by creating a Userdetails instance based on the relevant data of the User. (Note that I use the email as username)
#Service
public class UserServiceClass extends GenericServiceClass<User, UUID> {
#Autowired
public UserServiceClass(UserRepository userRepository) {
super(userRepository);
}
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Optional<User> selected = ((UserRepository) this.genericRepository).getUserByEmail(s);
if (selected.isPresent())
{
// Obtain user by email (username)
User user = selected.get();
// Obtain the roles of this user to construct the instance of UserDetails for SpringBoot Security.
Set<Role> roles = user.getRoles();
return org.springframework.security.core.userdetails.User
.withUsername(s)
.roles(roles.stream().toArray(
(n) -> {
return new String[n];
}
))
.password(user.getHashedPassword())
.build();
}
else
{
throw new UsernameNotFoundException("The user with email " + s + " is not registered in the database");
}
}
Finally, regarding AuthenticationManagerBuilder: This is a method that is used to configure authentication. As far as I know, you can define how your application should obtain the UserDetails. I am not sure if you can provide a method or a lambda to retrieve the triplet for authentication String username, String hashedPassword and boolean isAccountActive. What I do know and did in my application is provide the SQL query used to retrieve the triplet from my DB since I have there all that information. Code:
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationBuilder) throws Exception
{
Session session = this.sessionFactory.getCurrentSession();
session.beginTransaction();
authenticationBuilder.userDetailsService(this.userDetailsService()).passwordEncoder(this.passwordEncoder()).and()
.jdbcAuthentication().dataSource(this.dataSource)
.usersByUsernameQuery("select email, hashed_password as passw, true from user where email = ?")
.authoritiesByUsernameQuery("SELECT user.email, CONCAT(elementpermission.journal_id, '_', elementpermission.authority)\n" +
"FROM user, elementpermission\n" +
"WHERE elementpermission.user = user.uuid \n" +
"AND user.email = ?");
session.getTransaction().commit();
session.close();
}
TL;DR
Spring Security needs instances that fulfill the contract of the interface UserDetails because is the interface that Spring Security uses to obtain the relevant data for authentication.
The authentication manager builder is used to config howto obtain the data used for authentication.
You can check this links if you want better information:
https://www.baeldung.com/spring-security-jdbc-authentication
https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/jdbc.html
jdbcAuthentication() instead of inMemoryAuthentication() doesn't give access - Spring Security and Spring Data JPA
This is my first Question ever here on SO, it was helpfull and saved me lots of time, but now I can't find any solution to my problem.
As I'm rather new to spring and espacially to spring-security, I'm stuck with something that might be easy if i had more knowledge.
I have an existing Application that uses a local user database. It uses a custom UserDetails implementation that works if used with user:password authentification through a login form.
Here is the current setup:
public class SecurityContext extends WebSecurityConfigurerAdapter {
....
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider result = new DaoAuthenticationProvider();
result.setUserDetailsService(userDetailsService());
result.setPasswordEncoder(passwordEncoder());
return result;
}
#Override
#Bean
public GatesUserDetailsService userDetailsService() {
GatesUserDetailsService result = new GatesUserDetailsService();
result.setClientService(clientService);
result.setAccountService(accountService);
result.setCardService(cardService);
result.setPersonService(personService);
result.setAccountPropertyService(accountPropertyService);
result.setLoginAttemptService(loginAttemptService);
return result;
}
Now I want to use SSO from an external IDP that speaks OpenIdConnect.
Going through the documentation I was able to get this up and running in a "default" manner. That is, at the and of my process a get a user that is an Instance of OidcUser. I need that user to be either extended or incorporate the existing userDetails.
The documentation (Spring Boot and OAuth2) recommends to
Implement and expose OAuth2UserService to call the Authorization
Server as well as your database. Your implementation can delegate to
the default implementation, which will do the heavy lifting of calling
the Authorization Server. Your implementation should return something
that extends your custom User object and implements OAuth2User.
I was able to introduce my own Oauth2UserService that gets called right at the and of the authentification by setting:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.and()
.oauth2Login()
.failureHandler(authenticationFailureHandler())
.successHandler(authenticationSuccessHandler())
.userInfoEndpoint()
.userService(this.oauth2UserService())
.oidcUserService(this.oidcUserService());}
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
OidcUser oidcUser = delegate.loadUser(userRequest);
//..DO some additional Stuff check against external Server
//Here I could load my custom userDetails
GatesUserDetails userDetails = (GatesUserDetails) userDetailsService.loadUserByUsername("131:" + username);
....
But I have now Idea how to make my customUser a vaild return to my function.
I tried to implement the OidcUser Interface in my userDetails, but still it does not work.
Any hint (even to a more understandable doc) would be highly appreciated.
EDIT
To clarify things, I implemented the oidcUser Interface as stated in the docs along with the necessary implementations (getAttribute, getAttributes, getAuthorities) but still I could not use this as the return type would still be our GatesUserDetails, no way (for me) to cast it to oidcUser
Have the same problem with spring-security-oauth2-client-5.6.2, after hours google and debugger it solved.
First, make sure your UserInfo entrypoint is correct in case you own the
Auth server.
Plus requested scopes contains any of profiles not
only openid.
Logic found here: OidcUserService::shouldRetrieveUserInfo
private boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) {
// Auto-disabled if UserInfo Endpoint URI is not provided
ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
if (StringUtils.isEmpty(providerDetails.getUserInfoEndpoint().getUri())) {
return false;
}
// The Claims requested by the profile, email, address, and phone scope values
// are returned from the UserInfo Endpoint (as described in Section 5.3.2),
// when a response_type value is used that results in an Access Token being
// issued.
// However, when no Access Token is issued, which is the case for the
// response_type=id_token,
// the resulting Claims are returned in the ID Token.
// The Authorization Code Grant Flow, which is response_type=code, results in an
// Access Token being issued.
if (AuthorizationGrantType.AUTHORIZATION_CODE
.equals(userRequest.getClientRegistration().getAuthorizationGrantType())) {
// Return true if there is at least one match between the authorized scope(s)
// and accessible scope(s)
return this.accessibleScopes.isEmpty()
|| CollectionUtils.containsAny(userRequest.getAccessToken().getScopes(), this.accessibleScopes);
}
return false;
}
Hope this could help someone.
I have two applications, in first application say A user login by username and password. I get logged in username by using spring authentication obejct in application A. Application A is making rest or soap api calls to application B. I am passing username from A to B application by adding username in header of rest or soap calls. Application B is making database calls, doing CRUD on SQL database. I want to save that username in updated by, modified by fields. I used bean to store username but when multiple users are using application it's getting overwritten. How this can be achieved? Thank you
In the following interceptor userSync will save my current logged in username, usernameGetter will be used to get current logged in username from http request. userSync and usernameGetter are singleton beans.
public class RequestInterceptor extends HandlerInterceptorAdapter {
private DomainUserSynchronizer userSynchronizer;
#Autowired
UsernameGetter usernameGetter;
#Autowired
private UserSynchronizer userSync;
private static final Logger LOGGER = LoggerFactory.getLogger(RequestInterceptor.class);
#Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
LOGGER.info("Inside after completion");
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
// do nothing
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
HttpSession session = request.getSession();
// Enumeration e = (Enumeration) (session.getAttributeNames());
//session.getAttribute("an");
//session.getAttribute("MYSPEAK_SESSION_HASH");
String username = usernameGetter.getUsernameFromSession(request);
if(username!=null && !username.isEmpty()){
LOGGER.info("Found username in request: "+ username);
LOGGER.info("Setting username in userSynchronizer: "+ username);
userSync.getCurrentUser().setName(username);
}
else{
LOGGER.info(" Username not found in request: ");
LOGGER.info("Setting default username in userSynchronizer: "+ userSync.getCurrentUser().getName());
}
return true;
}
public DomainUserSynchronizer getUserSynchronizer() {
return userSynchronizer;
}
public void setUserSynchronizer(DomainUserSynchronizer userSynchronizer) {
this.userSynchronizer = userSynchronizer;
}
}
I guess your issue is in the scope of the beans you've declared through Spring: https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
I would recommend you to check that all calls go through a max level of Request scope.
Another point to tackle in your issue is to confirm/rollback the transaction per each call by rest/soap you do. It could be possible that you're overwritting the entity of database without committing it.
To avoid also the overrides in the updates I would suggest to add a logging table in which you keep track of all modifications and you can only do inserts on it (immutable/audit rows)
I use Spring Security, and I found strange behavior of framework while login. Spring Security WebAuthenticationDetails has parameter sessionId which is getting from HTTP request, and it all should be good, but in fact REST request gives me another session id. If I will autowire HttpSession and then get session id from it, I will get Spring-like id. So it seems that I have two ids for one user. Is it correct? Or I missed something?
EDITED:
For example this class will gave some session id
public class AuthenticationEventListener implements ApplicationListener<AbstractAuthenticationEvent> {
#Autowired
HttpSession httpSession;
#Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
if (event instanceof AuthenticationSuccessEvent) {
LoggedUser loggedUser = (LoggedUser) event.getAuthentication().getPrincipal();
loggedUser.initSessionParams(event.getAuthentication());
String sessionId = httpSession.getId();
}
}
}
and this method will give another one:
#RequestMapping(value = "/chart")
public Map getTestStatusesChart(HttpServletRequest request) {
String sessionId= request.getSession(false).getId();
return null;
}
So the answer is next: with condition of security Spring change session id by default. To prevent such behavior you need to disable session-fixation-protection in Spring Security config. more info by link
Hi
I am currently playing with Guice and #SessionScoped. To give it more sense, I decided to build a (very simple) authentication process.
Below, I will explain each step I have done. Then I will ask you some questions.
[1] I have create an Identity class which represents a person (guest or user) :
#SessionScoped
public class Identity implements Serializable
{
private String uid;
private String name;
public boolean isAuthenticate()
{
return uid != null;
}
public void logout()
{
this.uid = null;
}
/*Setters-Getters*/
}
[2] Next, I created an Authentication class that log-in user:
public class Authentication
{
#Override
public Identity authenticate(String login, String password)
{
/*some code*/
Identity identity = new Identity();
identity.setUid(user.getId());
return identity;
}
}
[3] Then, in my Servlet, I log-in the user :
#RequestScoped
public class LoginAction
{
#Inject
Injector injector;
protected void login(HttpServletRequest req, HttpServletResponse resp)
{
Identity identity = injector.getInstance(Identity.class);
Authentication auth = new Authentication();
identity = auth.authenticate("login","password");
}
}
[4] Finally, I create a Filter that show me if user is authenticated :
#Singleton
public class SecurityFilter implements Filter
{
#Inject
private Injector injector;
#Override
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
{
Identity identity = injector.getInstance(Identity.class);
if(identity.isAuthenticate())
{
System.err.println("USER");
}
else
{
System.err.println("GUEST");
}
chain.doFilter(request, response);
}
}
Well, this code is not working. My Identity's uid is always "null".
Let's go for questions :
a - First of all, Why did my code not works ?
b - Is #SessionScoped equivalent to set the object in HttpSession ?
c - How to invalidate the Identity object (only it) in (http)session ?
d - Generally, In which case did we have to use #SessionScoped?
Thanks you for reading,
Waiting your answers.
[a] You're assigning a new instance of Identity to a local variable in LoginAction, not replacing the instance managed by Guice. You could solve the problem by populating the uid and name fields on the existing Identity instance managed by Guice.
For example, instead of
identity = auth.authenticate("login","password");
you could say:
Identity identity = injector.getInstance(Identity.class);
Authentication auth = new Authentication();
Identity authenticated = auth.authenticate("login","password");
identity.setUid(authenticated.getUid());
identity.setName(authenticated.getName());
There are cleaner ways to do it, but you get the idea.
[b]/[d] That's correct: #SessionScoped is equivalent to setting a variable in the HttpSession, and this is the kind of situation that you would use it. You'll need it for objects that need to be unique across sessions, but need to be available for every request.
[c] I'm not quite sure what you mean, but if you're wanting to redirect to different places in the app depending on whether the user is logged in, your filter design is a common way to do that.
Some improvements that you could make:
Have a SessionScoped service that manages the session's user's Identity, and make sure it's synchronized on the Identity instance. That way you won't have concurrency troubles if a user makes two requests in quick succession.
Prefer injecting Providers instead of injecting the Injector(examples here) to decouple your classes from Guice.
Inject dependencies into your classes' constructors, instead of injecting fields. This allows for easier testing (by providing mock/stub dependencies in tests).