I am trying to authenticate against AD in my application created with Vaadin, which is using also Spring (SpringVaadinIntegration).
I can't find any information about how to achieve this and a lot of confusing, different and partial ways to connect to Active Directory with Spring security.
Since Vaadin form fields don't have a name, I don't know if I can even use a normal form or I have to write my own JSP. My impression is that to map the username and the password entered in the form to the xml it's necessary that the fields have a name.
Has anybody achieved this or anybody has a clue on how to do it?
If somebody can provide a link where this is explained step by step, for dummies, would be great too. I just can find partial solutions, where you don't get an overall of the system and how should be configured.
We have a TextField (username), a PasswordField (password) and a Button on a UI:
public class MyUI extends UI {
#Override
protected void init( VaadinRequest request ) {
setContent( VaadinSession.getCurrent().getAttribute("userId") == null ? getNewLoginLayout() : getNewMainLayout() );
}
private VerticalLayout getNewLoginLayout() {
TextField username = ...
TextField password = ...
Button login = ...
return new VerticalLayout(username, password, login);
}
}
When the button pushed we do a simple LDAP search like this on the server side (for example pass these parameters to a Spring bean). If it is successful we set a VaadinSession attribute (userId) and change the UI content to the main layout. Spring security need not necessarily.
Even this question is already answered I want to show you my solution.
We use Spring Security for LDAP authentication, so we have these two configuration classes:
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, proxyTargetClass = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
// #formatter:off
http
.authorizeRequests()
.anyRequest().authenticated() // Alle Requests erfordern einen Login...
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/#!").permitAll() // http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#jc-form
.and()
.logout().permitAll() // http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#jc-logout
.and()
.csrf().disable(); // CSRF (https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html) wird von Vaadin selbst gehandhabt!
// #formatter:on
}
/**
* #see http://stackoverflow.com/questions/34944617/java-config-for-spring-security-with-vaadin/35212403#35212403
*/
#Override
public void configure(WebSecurity web) throws Exception
{
// #formatter:off
web
.ignoring()
.antMatchers("/resources/**", "/VAADIN/**");
// #formatter:on
}
}
#Configuration
public class SecurityConfigActiveDirectory
{
#Value("${ldap.url}")
String ldapUrl;
#Value("${ldap.domain}")
String ldapDomain;
#Bean
public AuthenticationManager authenticationManager()
{
ActiveDirectoryLdapAuthenticationProvider adProvider = new ActiveDirectoryLdapAuthenticationProvider(ldapDomain, ldapUrl);
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
adProvider.setAuthoritiesMapper(getAuthorityMapper());
return new ProviderManager(Arrays.asList(adProvider));
}
private static SimpleAuthorityMapper getAuthorityMapper()
{
SimpleAuthorityMapper mapper = new SimpleAuthorityMapper();
mapper.setConvertToUpperCase(true);
return mapper;
}
}
SecurityConfig class defines which pages should be protected in our web application and SecurityConfigActiveDirectory defines the LDAP authentication provider.
ldap.domain can be something like private.myTest.de and ldap.url something like ldap://myLdapHost.private.myTest.de:389.
Cheers!
Related
Vaadin 19 + Spring Boot + custom authentication flow
Working: login shown correctly + authentication succeeds + redirect to correct home page URL
Problem: on the homepage the login box is shown again
My implementation is based on https://vaadin.com/learn/tutorials/securing-your-app-with-spring-security/setting-up-spring-security
#Autowired
private UserService userService;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider(userService))
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Require login to access internal pages and configure login form.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
// Not using Spring CSRF here to be able to use plain HTML for the login page
http.csrf().disable()
// Register our CustomRequestCache that saves unauthorized access attempts, so
// the user is redirected after login.
.requestCache().requestCache(new CustomRequestCache())
// Restrict access to our application.
.and().authorizeRequests()
// Allow all flow internal requests.
.requestMatchers(SecurityUtil::isFrameworkInternalRequest).permitAll()
// Allow all requests by logged in users.
.anyRequest().authenticated()
// Configure the login page.
.and()
.formLogin()
.loginPage(URL_LOGIN).permitAll()
.loginProcessingUrl(URL_LOGIN_PROCESSING)
.failureUrl(URL_LOGIN_FAILURE)
.successHandler(new CustomAuthenticationSuccessHandler())
// Configure logout
.and()
.logout()
.logoutUrl(URL_LOGOUT)
.logoutSuccessUrl(URL_LOGOUT_SUCCESS);
}
/**
* Allows access to static resources, bypassing Spring security.
*/
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(
// Vaadin Flow static resources
"/VAADIN/**",
// the standard favicon URI
"/favicon.ico",
// the robots exclusion standard
"/robots.txt",
// style related files, needed here to prevent download of files between login and home
"/icons/**",
"/images/**",
"/fonts/**",
"/styles/**",
"/sw-runtime-resources-precache.js",
// web application manifest
"/manifest.webmanifest",
"/sw.js",
"/offline-page.html",
// (development mode) static resources
"/frontend/**",
// (development mode) webjars
"/webjars/**",
// (production mode) static resources
"/frontend-es5/**", "/frontend-es6/**");
}
The logging shows repeated calls to CustomAuthenticationProvider to this method:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
Could this be a missing user role? Does this need to be configured?
My application worked OK before, when I was first testing with a single fixed account like this:
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails user = User.withUsername("username")
.password("{noop}password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
Check if you exclude the LoginView in ConfigureUIServiceInitListener.java
private void beforeEnter(BeforeEnterEvent event) {
if (!LoginView.class.equals(event.getNavigationTarget()) // <--
&& !SecurityUtils.isUserLoggedIn()) {
event.rerouteTo(LoginView.class);
}
}
As I already suspected, problem was a missing ROLE.
In my CustomAuthenticationProvider, I was returning a user like this:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
var email = authentication.getName();
var password = authentication.getCredentials().toString();
// Call here to backend to check login
return new UsernamePasswordAuthenticationToken(email, password);
}
Which actually returns a user which is authenticated=false...
By changing the code to:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
var email = authentication.getName();
var password = authentication.getCredentials().toString();
// Call here to backend to check login
Collection<GrantedAuthority> roles = new ArrayList<>();
roles.add(new SimpleGrantedAuthority("USER"));
return new UsernamePasswordAuthenticationToken(email, password, roles);
}
the user has authenticated=true, and the Vaadin UI works as expected.
This question already has answers here:
Spring Security : Multiple HTTP Config not working
(2 answers)
Closed 3 years ago.
I have followed the instructions below to create two different http security blocks for admin and user.
docs.spring.io/spring-security-multiple-httpsecurity
As document says , if the URL does not start with /aaa, another configuration will be used to pattern.
But when i put #Order(1) at admin block ,admin page works fine , user page will not redirect to login page /login/user
while i put #Order(1) at user block , user page works fine , admin page will not redirect to login page /login/admin either.
here is my java code
#EnableWebSecurity
public class MultiHttpSecurityConfig {
/**
* intercept user url
*/
#Configuration
#Order(1)
public static class UserWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationSuccessHandler successHandler;
#Autowired
CustomAuthenticationFailureHandler failureHandler;
#Autowired
private CustomAuthenticationProvider customAuthProvider;
#Autowired
private CustomUserDetailsService userDetailsService;
#Value("${my.cookie.timeout}")
private int cookieTimeOut;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/images/**, /fonts/**").permitAll()
.antMatchers("/bbb/**","/aaaa/**").hasAnyRole("USER");
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.loginPage("/login/user").permitAll();
http.logout().permitAll();
http.rememberMe().key("uniqueAndSecret").tokenValiditySeconds(cookieTimeOut);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthProvider);
auth.userDetailsService(userDetailsService);
}
}
/**
* intercept admin url
*/
#Configuration
public static class AdminWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationSuccessHandler successHandler;
#Autowired
CustomAuthenticationFailureHandler failureHandler;
#Value("${my.cookie.timeout}")
private int cookieTimeOut;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/images/**, /fonts/**").permitAll()
.antMatchers("/ccc/**","/dddd").hasAnyRole("ADMIN");
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.loginPage("/login/admin").permitAll();
http.logout().permitAll();
http.rememberMe().key("uniqueAndSecret").tokenValiditySeconds(cookieTimeOut);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("test").password("test").roles("ADMIN");
}
}
}
update:
As dur says below,the key reason is authorizeRequests() method matches all urls in Order(1) , so i need to add antMatcher("/bbb/*")** at first before authorizeRequests().
But antMatcher() only matches just only one kind of url , if i have one more kinds of urls to match like "/bbb/" , "/aaa/*" , how to achieve this ?
Then i need to add one more WebSecurityConfigurerAdapter configuration ?
Is there any better way to do this to reduce code ?
I have found solution in spring-security SDK requestMatchers() method, it provides a example above the requestMatchers() method.
Here is my code below for match user's urls at Order(1)
http.csrf().disable();
http.requestMatchers()
.antMatchers("/bbb/**", "/aaa/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasAnyRole("USER");
http.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.loginPage("/login/user").permitAll();
http.logout().permitAll();
Then both bbb and aaa have been matched and don't need to create another configuration
But another problem occurs , it will show "405 method not allowed" when post username and password to login/user interface at user login page , while admin page works fine
I have searched google ,it tells to disable csrf , but i have already disable csrf...
In one of my project I did not use form login, but implemented a custom AccessDeniedHandler and AuthenticationEntryPoint that can redirect to different login pages with some custom logic I needed. However, loginPage() is also an AuthenticationEntryPoint in the end.
They can be added via:
.exceptionHandling().authenticationEntryPoint(new YourCustomAuthEntryHandler()).and()
.exceptionHandling().accessDeniedHandler(new YourCustomAccessDeniedHandler())
It's just an idea, maybe it worth checking them.
Moreover, I think you will need authenticate all requests, add this line for both configs in the authorizeRequests() block:
.anyRequest().authenticated()
I am using spring boot 2.1.4 with dependencies of actuator. I wanted to configure separate authentication and authorization mechanisms for actuator and my application. I read the Multiple HttpSecurity and configured my WebSecurityAdapter as follows:
#Configuration
public class ProvisioningServiceSecurityConfiguration {
#Value("${actuator.user.name}")
private String actuatorUserName;
#Value("${actuator.password}")
private String actuatorPassword;
#Value("${actuator.role}")
private String actuatorRole;
#Bean
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("password").roles("ADMIN").build());
manager.createUser(
users.username(actuatorUserName).password(actuatorPassword).roles(actuatorRole).build());
return manager;
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/locations/**")
.antMatcher("/organizations/**")
.antMatcher("/productTypes/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic();
}
}
#Configuration
#Order(2)
public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/manage/**")
.authorizeRequests()
.anyRequest().hasRole("ACTUATOR_ADMIN")
.and()
.httpBasic();
}
}
/*#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}*/
}
Note: I have disabled form Login temporarily
When I run a curl request
curl -XGET http://localhost:9797/provisioningService/organizations/all
I am able to see the output. Its as though the spring security never existed. When I enable form login, I get the spring login screen. The other behavior that I observed is if I interchange the username and password of /locations with the actuator username and password, I still get a valid response back.
I understand the form login is more of a fallback but I want to disable the form login (probably we may move to cas) and use authentication and authorization only based on the spring security httpBasic. I am not able to understand the mistake I am making.
My requirement is finally :
1) a request to /organizations or /locations etc should be accessible only if the username password is "user" and "password"
2) a request to /manage which is the actuator api should be accessible only if the username and password and role matches with the actuator username and password.
3) Any other API can be permitAll / form login
How do i go about achieving this?
1) Spring Security has a function to control access by filtering by Authorities(after Authentication), but there is no function to filter by the information required for login. You need business logic to verify that you are attempting to log in with the corresponding ID and password during login.
2) As mentioned above, access control with ID and password is not provided.
I recommend creating Authorities for only the two accounts you requested.
3) .antMatcher("/form").permitAll()
I'm trying to integrate vaadin 10 with spring security (using the spring project base provided by vaadin), and I'm confused on how they interact exactly. If I go to a protected url (in this example, "/about") typing it directly in the browser, the login page shows up. If I go to the same URL by clicking in a link from the UI, the page shows up even if I'm not authenticated. So I guess that Vaadin is not going through Spring Security's filter chain, but then how do I secure my resources inside the UI, and how can I share the authenticated user between vaadin and spring? Am I supposed to implement security twice? The documentation available doesn't seem to cover this, and every link on the internet has examples with Vaadin 7-8, which I've never used and seems to work differently from 10+.
Does anyone know any resource about this, or can you enlighten me on how all of this works together so I can know what I'm doing?
Here's my security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] ALLOWED_GET_URLS = {
"/",
//"/about",
"/login/**",
"/frontend/**",
"/VAADIN/**",
"/favicon.ico"
};
private static final String[] ALLOWED_POST_URLS = {
"/"
};
//#formatter:off
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.mvcMatchers(HttpMethod.GET, ALLOWED_GET_URLS)
.permitAll()
.mvcMatchers(HttpMethod.POST, ALLOWED_POST_URLS)
.permitAll()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.permitAll();
}
//#formatter:on
}
Using Vaadin Flow (12.0.2), Spring Boot Starter (2.0.2.RELEASE) and Spring Boot Security, basically, I found authorizing based on role/authority using the following ways;
Route/Context based role/authority managment
Spring security (HttpSecurity)
Vaadin API (BeforeEnterListener and Route/Navigation API)
Business unit role/authority management
Inside the code using HttpServletRequest.isUserInRole method
Let's start with a simple example of Spring Security configuration;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig
extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // CSRF is handled by Vaadin: https://vaadin.com/framework/security
.exceptionHandling().accessDeniedPage("/accessDenied")
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.and().logout().logoutSuccessUrl("/")
.and()
.authorizeRequests()
// allow Vaadin URLs and the login URL without authentication
.regexMatchers("/frontend/.*", "/VAADIN/.*", "/login.*", "/accessDenied").permitAll()
.regexMatchers(HttpMethod.POST, "/\\?v-r=.*").permitAll()
// deny any other URL until authenticated
.antMatchers("/**").fullyAuthenticated()
/*
Note that anonymous authentication is enabled by default, therefore;
SecurityContextHolder.getContext().getAuthentication().isAuthenticated() always will return true.
Look at LoginView.beforeEnter method.
more info: https://docs.spring.io/spring-security/site/docs/4.0.x/reference/html/anonymous.html
*/
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password("$2a$10$obstjyWMAVfsNoKisfyCjO/DNfO9OoMOKNt5a6GRlVS7XNUzYuUbO").roles("ADMIN");// user and pass: admin
}
/**
* Expose the AuthenticationManager (to be used in LoginView)
* #return
* #throws Exception
*/
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
As you see, I have not specified any permission based on role on any of my routed views (annotated with #Route) yet. What I will do is if I have a routed view, I will register a BeforeEnterListener when it (the routed view) is being constructed and will check the required role/privilege there.
The following is an example to check if the user has ADMIN role before navigating to admin-utils view;
#Route(value = "admin-utils")
public class AdminUtilsView extends VerticalLayout {
#Autowired
private HttpServletRequest req;
...
AdminUtilsView() {
...
UI.getCurrent().addBeforeEnterListener(new BeforeEnterListener() {
#Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
if (beforeEnterEvent.getNavigationTarget() != DeniedAccessView.class && // This is to avoid a
// loop if DeniedAccessView is the target
!req.isUserInRole("ADMIN")) {
beforeEnterEvent.rerouteTo(DeniedAccessView.class);
}
}
});
}
}
In case the user has not the ADMIN role, (s)he will be routed to DeniedAccessView which is permitted already for all in the Spring Security configuration.
#Route(value = "accessDenied")
public class DeniedAccessView
extends VerticalLayout {
DeniedAccessView() {
FormLayout formLayout = new FormLayout();
formLayout.add(new Label("Access denied!"));
add(formLayout);
}
}
In the above example (AdminUtilsView ), you can also see a use case for HttpServletRequest.isUserInRole() in Vaadin code by autowiring the HttpServletRequest.
SUMMARY: If your view has a Route, use BeforeEnterListener to Authorize the request first, otherwise use Spring Security
matchers (e.g. regexMatchers or antMatchers) for rest services and etc. .
NOTE: Using both the Vaadin Route and Spring Security matcher rules together for the same rule might be a bit twisted and I don't suggest that (it causes some internal loops in Vaadin; e.g. imagine we have a view routed with /view and an entry in Spring Security for /view with a required role. If a user is missing such role and (s)he is routed/navigated to such page (using Vaadin routing API), Vaadin tries to open the view associated with the route while Spring security avoids that due to the missing role).
Also, I think, using Vaadin flow navigation API a good practice before rerouting or navigating the user to a different view/context would be to check for the required role/authority.
Moreover, to have an example of using AuthenticationManager in Vaadin, we can have a Vaadin based LoginView similar to;
#Route(value = "login")
public class LoginView
extends FlexLayout implements BeforeEnterObserver {
private final Label label;
private final TextField userNameTextField;
private final PasswordField passwordField;
/**
* AuthenticationManager is already exposed in WebSecurityConfig
*/
#Autowired
private AuthenticationManager authManager;
#Autowired
private HttpServletRequest req;
LoginView() {
label = new Label("Please login...");
userNameTextField = new TextField();
userNameTextField.setPlaceholder("Username");
UiUtils.makeFirstInputTextAutoFocus(Collections.singletonList(userNameTextField));
passwordField = new PasswordField();
passwordField.setPlaceholder("Password");
passwordField.addKeyDownListener(Key.ENTER, (ComponentEventListener<KeyDownEvent>) keyDownEvent -> authenticateAndNavigate());
Button submitButton = new Button("Login");
submitButton.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
authenticateAndNavigate();
});
FormLayout formLayout = new FormLayout();
formLayout.add(label, userNameTextField, passwordField, submitButton);
add(formLayout);
// center the form
setAlignItems(Alignment.CENTER);
this.getElement().getStyle().set("height", "100%");
this.getElement().getStyle().set("justify-content", "center");
}
private void authenticateAndNavigate() {
/*
Set an authenticated user in Spring Security and Spring MVC
spring-security
*/
UsernamePasswordAuthenticationToken authReq
= new UsernamePasswordAuthenticationToken(userNameTextField.getValue(), passwordField.getValue());
try {
// Set authentication
Authentication auth = authManager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
/*
Navigate to the requested page:
This is to redirect a user back to the originally requested URL – after they log in as we are not using
Spring's AuthenticationSuccessHandler.
*/
HttpSession session = req.getSession(false);
DefaultSavedRequest savedRequest = (DefaultSavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
String requestedURI = savedRequest != null ? savedRequest.getRequestURI() : Application.APP_URL;
this.getUI().ifPresent(ui -> ui.navigate(StringUtils.removeStart(requestedURI, "/")));
} catch (BadCredentialsException e) {
label.setText("Invalid username or password. Please try again.");
}
}
/**
* This is to redirect user to the main URL context if (s)he has already logged in and tries to open /login
*
* #param beforeEnterEvent
*/
#Override
public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//Anonymous Authentication is enabled in our Spring Security conf
if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken)) {
//https://vaadin.com/docs/flow/routing/tutorial-routing-lifecycle.html
beforeEnterEvent.rerouteTo("");
}
}
}
And finally, here is the logout method that can be called from a menu or button:
/**
* log out the current user using Spring security and Vaadin session management
*/
void requestLogout() {
//https://stackoverflow.com/a/5727444/1572286
SecurityContextHolder.clearContext();
req.getSession(false).invalidate();
// And this is similar to how logout is handled in Vaadin 8:
// https://vaadin.com/docs/v8/framework/articles/HandlingLogout.html
UI.getCurrent().getSession().close();
UI.getCurrent().getPage().reload();// to redirect user to the login page
}
You can continue completing the role management using Spring UserDetailsService and creating a PasswordEncoder bean by looking at the following examples:
https://github.com/igor-baiborodine/vaadin-demo-bakery-app/blob/master/src/main/java/com/kiroule/vaadin/bakeryapp/app/security/SecurityConfiguration.java
https://github.com/igor-baiborodine/vaadin-demo-bakery-app/blob/master/src/main/java/com/kiroule/vaadin/bakeryapp/app/security/UserDetailsServiceImpl.java
https://www.baeldung.com/role-and-privilege-for-spring-security-registration
I am writing an application using Spring Security.
I've implemented my custom UserDetails, UserDetailsService, AccessDecisionVoter and WebSecurityConfigurerAdapter.
I want to permit unauthorized users to access the /authenthication/login page to log in, but every other access to a page needs to be handled by the custom AccessDecisionVoter.
My custom WebSecurityConfigurerAdapter class looks like this:
#Configuration
#EnableWebSecurity
#ComponentScan(value = "security")
public class Configuration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/authentication/login**")
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.accessDecisionManager(accessDecisionManager())
;
}
#Bean
public AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters =
Arrays.asList(new AccessDecisionVoterImpl());
return new UnanimousBased(decisionVoters);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
try {
auth.userDetailsService(userDetailsServiceImpl);
} catch (Exception e) {
e.printStackTrace();
}
}
#Bean
public UserDetailsServiceImpl userDetailsService() {
return userDetailsServiceImpl;
}
}
I have defined several roles in my database. The vote method in my custom AccessDecisionVoter retrieves the permissions of the User that is logged in and grants or denies access, based on that and the URL + httpMethod.
Problem:
However when I send a POST to the /authentication/login with the username and password my code gives me a NullPointerException in the custom AccessDecisionVoter: the username (retrieved via the authentication.getPrincipal(); returns anonymousUser, which results in the NullPointer later in the code. But I don't understand why the vote method gets called anyway since the configuration file told Spring to permittAll accesses to /authentication/login
I believe that the way you have it set up, permitAll just makes it so that all requests are allowed (even unauthenticated ones) but it doesn't disable the security chain for that request, it will still try to process through them all. If you just want to bypass all the security, the approach i usually use is to override the webSecurity config method and add the exception cases there, like so:
#Override
public void configure(WebSecurity webSecurity) throws Exception
{
webSecurity
.ignoring()
.antMatchers("/authentication/login**");
}