I have an Vaadin 7 application working with Spring and currently there is one ADMIN role that can log in into the system and see all views. Now I need to add another role let's say CO_ADMIN and allow user with this role to view part of views. What is the best way to achieve this?
Thanks
If you use spring security you can make a helper class with the following methods:
public static boolean hasRole(String role) {
return getPrincipal().getAuthorities().contains(new SimpleGrantedAuthority(role));
}
public static UserDetails getPrincipal() {
return (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
Then you can define what roles each part of your view needs programatically:
Panel panel = new Panel("securedpanel");
if (SecurityUtils.hasRole("ADMIN")) {
//panel.setVisible(false);
panel.setEnabled(false);
}
If you want to secure the whole view, you can enable the #Secured annotation in the spring security config xml:
<global-method-security secured-annotations="enabled" />
With #Secured enabled you can annotate your views like this:
#Secured("{ADMIN}")
This way if the user navigates to the view, it responds with 403 permission denied. You can extend your navigator to handle this by redirecting to another view or define an errorhandler for your application.
Related
I am pretty new in Spring Security and I am working on a Spring Boot project that uses Basic Authentication in order to protect some APIs. I am starting from an existing tutorial code (a Udemy course) trying to adapt it to my own use cases.
In this project I have this SecurityConfiguration used to configure the basic authentication.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
private static String REALM = "REAME";
private static final String[] USER_MATCHER = { "/api/utenti/cerca/**"};
private static final String[] ADMIN_MATCHER = { "/api/utenti/inserisci/**", "/api/utenti/elimina/**" };
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests()
.antMatchers(USER_MATCHER).hasAnyRole("USER")
.antMatchers(ADMIN_MATCHER).hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint()).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public AuthEntryPoint getBasicAuthEntryPoint()
{
return new AuthEntryPoint();
}
/* To allow Pre-flight [OPTIONS] request from browser */
#Override
public void configure(WebSecurity web)
{
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
#Bean
public BCryptPasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
};
#Bean
#Override
public UserDetailsService userDetailsService()
{
UserBuilder users = User.builder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users
.username("ReadUser")
.password(new BCryptPasswordEncoder().encode("BimBumBam_2018"))
.roles("USER").build());
manager.createUser(users
.username("Admin")
.password(new BCryptPasswordEncoder().encode("MagicaBula_2018"))
.roles("USER", "ADMIN").build());
return manager;
}
}
So from what I have understand:
Here it id defined the list of API that can be accessed by a nornmal user and the list of API that can be accessed by and admin user:
private static final String[] USER_MATCHER = { "/api/utenti/cerca/**"};
private static final String[] ADMIN_MATCHER = { "/api/utenti/inserisci/**", "/api/utenti/elimina/**" };
Into the previous configure() method basically it is stating that the API URL matching with the USER_MATCHER are accessible by logged user having role USER while API having URL matching ADMIN_MATCHER are accessible by logged user having role ADMIN. Is this interpretation correct?
Finnally the UserDetailsService bean simply define two users: one belonging to the USER "group" and the other one belonging to both the USER and ADMIN "group".
So, if I well understood, the first one will be aple only to access to the API having enpoint URL /api/utenti/cerca/** while the second one will be able to access also to the APIs having endpoint URLs /api/utenti/inserisci/** and /api/utenti/elimina/**
Is it my reasoning correct?
And now my doubt: into a controller class of this project I defined this method:
#RestController
#RequestMapping("api/users")
#Log
public class UserController {
#Autowired
UserService userService;
//#Autowired
//private BCryptPasswordEncoder passwordEncoder;
//#Autowired
//private ResourceBundleMessageSource errMessage;
#GetMapping(value = "/test", produces = "application/json")
public ResponseEntity<String> getTest() throws NotFoundException {
log.info(String.format("****** getTest() START *******"));
return new ResponseEntity<String>("TEST", HttpStatus.OK);
}
..............................................................................................................
..............................................................................................................
..............................................................................................................
}
As you can see this method handling a GET request toward the localhost:8019/api/users/test endpoint.
This endpoint URL is not in any of the previous two list related the protected endpoint (it is not into the USER_MATCHER list neither into the ADMIN_MATCHER list. So I expected that simply this endpoint was not protected and accessible to everyone. But performing the previous request using PostMan, I obtain this error message:
HTTP Status 401 : Full authentication is required to access this resource
So basically it seems to me that also if this endpoint not belong to any protected endpoint list it is in some way protected anyway (it seems to me that at least the user must be authenticated (infact trying both the previous user I can obtain the expected output, so it should mean that the endpoint is not protected by the user rule but it is protected againts not authenticated access).
Why? Maybe it depende by the previous configure() method settings, in particular this line?
.anyRequest().authenticated()
In case is it possible to disable in some way to implement something like this:
If a called endpoint belong to one of the previous two lists (USER_MATCHER and ADMIN_MATCHER) --> the user must be authenticated and need to have the correct role.
If a called endpoint not belong to one of the previous lists --> everybody can access, also not authenticated user.
This approach make sense or am I loosing something?
I take this occasion to ask you also another information: do you think that it is possible to configure Spring security of this specific project in order to protect some specific endpoints using the basic authentication and some other specific endpoints using the JWT authentication.
Sone further notes to explain why this last question. This project is a microservice that at the moment is used by another microservice (used to generate JWT token) in order to obtain user information. (the other microservice call an API of this project in order to receive user information so it can generate a JWT token that will be used in my application. The comunication between these 2 microservice must use basic authentication).
Since this project contains all the entity classes used to map the tables related to the users on my DB, my idea was to use this project also for generic user management, so it could include functionality like: add a brand new user, changes information of an existing user, obtain the list of all the users, search a specific user, and so on.
These new APIs will be protected by JWT token because each API can be called from a specific user type having different privileges on the system.
So I am asking if in a situation like this I can add without problem 2 different types of authentication (basic authentication for the API that retrieve a user so the other microservice can obtain this info) and JWT authentication for all the other APIs. It make sense or is it better to create a brand new project for a new user management microservice?
So, if I well understood, the first one will be aple only to access to the API having enpoint URL /api/utenti/cerca/** while the second one will be able to access also to the APIs having endpoint URLs /api/utenti/inserisci/** and /api/utenti/elimina/**
Yes.
Why? Maybe it depende by the previous configure() method settings, in particular this line?
Yes, when using .anyRequest().authenticated(), any requests that have not been matched will have to be authenticated.
If a called endpoint not belong to one of the previous lists --> everybody can access, also not authenticated user.
You can achieve this by doing anyRequest().permitAll(). But this is not so secure because you are allowing access to every other endpoints, instead you should stay with anyRequest().authenticated() and allow access to specific endpoints manually, like so:
http
.authorizeRequests()
.antMatchers(USER_MATCHER).hasAnyRole("USER")
.antMatchers(ADMIN_MATCHER).hasAnyRole("ADMIN")
.antMatchers("/api/users/test").permitAll()
.anyRequest().authenticated()
...
I am using #Secured("ADMIN"), my role definition is also ADMIN (not ROLE_ADMIN). When I access the API, I am not getting the expected value, it says access denied.
Below is the code, how I have overridden ROLE_ value
#PostMapping("/users")
#Loggable
#Secured({"Administrator"})
public ResponseEntity<?> createUser( #Valid #RequestBody User userRequest) {
.....
}
Implemented CustomAccessDecisionManager where the role prefix (setRolePrefix("")) was set to empty and the CustomDecisionManager was configured in my security configuration file.
The empty role prefix is working fine, the code is below for that
.authorizeRequests().anyRequest().authenticated()
.accessDecisionManager(customAD()).hasRole("ADMIN")
But it's not working when I use #Secured.
My DB contains role as USER1, USER2.
Any help is greatly appreciated. Thank you.
I was making an application with Spring which is providing the backend with the REST Api's and Angular managing the views part of the Application.
I had a couple of questions.
I was thinking of maintaining a sessions in the app so that I can track the logged in Users and also know when they logout and other things. Moreover the Api's should be authenticated using token.
My setup is Spring + Angular and PostgreSQL for Database and Hibernate as ORM.
To track login - You need to define a Spring Bean which implements org.springframework.context.ApplicationListener.
Then, in your code, do something like this:
import org.springframework.context.ApplicationListener;
#Component
public class myLoginListener implements ApplicationListener<ApplicationEvent> {
public void onApplicationEvent(ApplicationEvent appEvent)
{
if (appEvent instanceof AuthenticationSuccessEvent)
{
AuthenticationSuccessEvent event = (AuthenticationSuccessEvent) appEvent;
UserDetails userDetails = (UserDetails) event.getAuthentication().getPrincipal();
//track the logged in Users here ....
}
}
2.To track logout - write a listener by implementing HttpSessionListener and use Spring Security as below..
sessionDestroyed() will be called just before the session is going to destroyed.
#Component
public class mySessionListener implements javax.servlet.http.HttpSessionListener{
#Override
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
SecurityContextImpl springSecurityContext = (SecurityContextImpl)session.getAttribute("SPRING_SECURITY_CONTEXT");
if(springSecurityContext!=null){
Authentication authentication = springSecurityContext.getAuthentication();
LdapUserDetails userDetails = (LdapUserDetailsImpl)authentication.getPrincipal();
//track user logout here
}
...
You can refer this tutorial - Secure AnugularJS applications with spring security
and this tutorial from the official site.
Take a look into Spring security framework:
spring security official documentation
Spring security getting started example
My requirement is as below:
In our application the user's credentials are validated against the database(not using spring security since it is a legacy application) for the first time. If the user is a valid user, he will be logged into the application. Once the user logs into the application he can make few rest calls. Now, I want to once again validate the user's credentials by using spring security before making any rest call. Here, the challenge is we should not redesign the database schemas. We need to use a stored procedure which validates the user. This particular stored procedure returns an error message if authentication fails, otherwise nothing is returned. There are no roles defined in this case. Just simple authentication using a stored procedure. Now, I want to accomplish this whole thing by spring security. May be writing a java class/ custom spring framework's class and in which the stored procedure is called and using that class in spring security configuration files. Can anybody suggest ideas on how to start up with please?
I have implemented AuthenticationProvider. The following is the *security.xml.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/rest/*" access="permitAll"></intercept-url>
</http>
<authentication-manager >
<authentication-provider ref="csAuthenticationProvider" />
</authentication-manager>
But, the security framework is looking for roles. In my case there are no roles defined. As I said earlier, the user is authenticated for the first time without using spring framework. If the user wants to make any rest call, the spring security needs to re authenticate the user. It doesn't mean that the user needs to re enter credentials. The user's credentials are available in the rest call/request since he is already authenticated. The only thing needs to be done is I need to use the credentials by using request and re validate using the stored procedure. of course, using AuthenticationProvider may be a good idea, but the parameter "Authentication authentication" of the authenticate(Authentication authentication) method is not useful for me since I need to call my own stored procedure call again. for time being, I did not used the Authentication object, but instead used the stored procedure calling code in the authenticate() method. But, strangely, authenticate() method is not getting called. I am surprised and in confusion. Does any body has any ideas on where I am doing wrong?
Sounds like you need to implement an Authentication Provider. Here's a pretty simple example that I think you could adapt to call your stored procedure.
http://danielkaes.wordpress.com/2013/02/20/custom-authentication-provider-in-spring/
You can implement your own UserDetailsService and configure spring to use it.
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsServiceImpl"/>
</security:authentication-manager>
You need to create a custom UserDetailsService implementation, that will check against the DB.
Here is an example UserDetailsService implementation that does just that:
#Service("userService")
public class UserDetailsServiceImpl implements UserDetailsService, InitializingBean {
#Autowired
private AccountService accountService;
public void afterPropertiesSet() throws Exception {
}
#Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
username = username.toLowerCase();
try {
Account account = accountService.loadUserAccountByEmail(username);
if (account == null) {
throw new UsernameNotFoundException("Could not find email: " + username + "in the DB.");
}
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
for (Role r : account.getRoles()) {
auths.add(new SimpleGrantedAuthority(r.getRole()));
}
ApplicationUser user = null;
try {
user = new ApplicationUser(new Long(account.getId()), username, account.getPassword(), true, true, true, true, auths);
} catch (Exception ex) {
ex.printStackTrace();
}
return user;
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException(username + "not found", e);
}
}
}
Which I config in code like so:
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsServiceImpl)
.passwordEncoder(bCryptPasswordEncoder());
}
(you can also see a blog post I wrote about switching from xml to #annotation config for spring security referncing that project here: http://automateddeveloper.blogspot.co.uk/2014/02/spring-4-xml-to-annotation-configuration.html)
Using spring-webmvc and spring-security-web of version 3.2, I'd like to return different views depending on the user role (or whether user is authenticated or not), so that for a "/" request a user of role ANONYMOUS (or not authenticated user) gets the welcome page and a user of role USER gets the home page.
My current approach is doing this with a regular controller:
#Controller
public class WelcomeCtrl {
#RequestMapping("/")
public String welcome(Principal principal) {
if (userAuthenticated(principal)) {
return "redirect:home";
}
return "welcome";
}
private boolean userAuthenticated(Principal principal) {
return principal != null && principal instanceof Authentication
&& hasUserRole((Authentication) principal);
}
private boolean hasUserRole(Authentication principal) {
Collection<? extends GrantedAuthority> authorities = (principal)
.getAuthorities();
return Iterables.contains(Collections2.transform(authorities,
new Function<GrantedAuthority, String>() {
#Override
public String apply(GrantedAuthority authority) {
return authority.getAuthority();
}
}), "ROLE_USER");
}
}
However, I don't really like it because I feel that this redirection should be done with spring security (am I wrong?). Do you know any way of doing this with Spring Security configuration? My current config is as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/welcome").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.defaultSuccessUrl("/home").permitAll()
.and().logout().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder)
throws Exception {
authManagerBuilder.inMemoryAuthentication().withUser("user")
.password("password").roles("USER");
}
}
According to me, this is the best way to use spring security configuration latest in the market.
General support for Java Configuration was added to Spring framework in Spring 3.1. Since Spring Security 3.2 there has been Spring Security Java Configuration support which enables users to easily configure Spring Security without the use of any XML.
If you are familiar with the Security Namespace Configuration then you should find quite a few similarities between it and the Security Java Configuration support and you can use Security Namespace Configuration as well which suites you
There are many new things added to the spring security 3.2 you can use that
like
1). Java Configuration -- better for those who are more familiar to read the jar files to apply the configuration methods.
2). Concurrency Support -- In most environments, Security is stored on a per Thread basis. This means that when work is done on a new Thread, the SecurityContext is lost. Spring Security provides some infrastructure to help make this much easier for users. Spring Security provides low level abstractions for working with Spring Security in multi threaded environments. In fact, this is what Spring Security builds on to integration with AsyncContext.start(Runnable) and Spring MVC Async Integration.
3). CSRF Attacks :- Assume that your bank’s website provides a form that allows transferring money from the currently logged in user to another bank account. For example, the HTTP request might look like:
POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.abc.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account=9876
Now pretend you authenticate to your bank’s website and then, without logging out, visit an evil website. The evil website contains an HTML page with the following form:
<form action="https://bank.example.com/transfer" method="post">
<input type="hidden"
name="amount"
value="100.00"/>
<input type="hidden"
name="account"
value="evilsAccountNumber"/>
<input type="submit"
value="ohh you Win Money!"/>
</form>
You like to win money, so you click on the submit button. In the process, you have unintentionally transferred $100 to a malicious user. This happens because, while the evil website cannot see your cookies, the cookies associated with your bank are still sent along with the request.
such type of the attacks can easily be stopped by using the spring security 3.2, as this new feature Cross Site Request Forgery (CSRF) Protection in spring security generate some token which are matched at the payment gateway side.
and many more advantages......................