I a have working spring security project with vaadin session based on some github project. All is working fine until I create a new configuration static class where I want to specify the path where SSL should be required.
Here is my project and Application class in its original working state:
https://github.com/czetus/dluznikApp/blob/master/src/main/java/com/danes/main/Application.java
Added code to Application.java into first static class
public static class SecurityConfiguriation extends GlobalMethodSecurityConfiguration
#EnableWebSecurity
public static class WebSecurity extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel()
.antMatchers("/v1*").requiresSecure();
}
}
The project is compiling and deploying wihtout any error. Problem starts when I start localhost:8080. I receive an exception defined here:
https://github.com/czetus/dluznikApp/blob/master/src/main/java/com/danes/main/servlet/VaadinSessionSecurityContextHolderStrategy.java .
java.lang.IllegalStateException: No VaadinSession bound to current thread
at com.danes.main.servlet.VaadinSessionSecurityContextHolderStrategy.getSession(VaadinSessionSecurityContextHolderStrategy.java:41) ~[classes/:na]
at com.danes.main.servlet.VaadinSessionSecurityContextHolderStrategy.clearContext(VaadinSessionSecurityContextHolderStrategy.java:12) ~[classes/:na]
at org.springframework.security.core.context.SecurityContextHolder.clearContext(SecurityContextHolder.java:73) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:180) ~[spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
...
If I remove annotation #EnableWebSecurity there is no error and ssl is not working.
When I was debugging I noticed that getSession is invoked too early in method
#Override
public void setContext(SecurityContext context) {
getSession().setAttribute(SecurityContext.class, context);
}
in class VaadinSessionSecurityContextHolderStrategy.java
So what do I have to do or is there some other way to not create this configuration class and get this path (pattern) to be secured by SSL ?
Could you skip using VaadinSessionSecurityContextHolderStrategy? The default Spring security setup with thread local strategy should work.
Edit
Security context is typically stored in the thread that serves the request. Security filter is run before Vaadin servlet gets the request and this means that the session will not exist yet so the security filter cannot use Vaadin session to store the security context.
You can add relevant user data to Vaadin session with session init listener When it gets called the filter has already added user information to the thread local security context holder.
#Component("vaadinServlet")
#WebServlet(urlPatterns = "/*", name = "MyVaadinServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = MyUi.class, productionMode = false)
public class MyVaadinServlet extends SpringVaadinServlet {
private static final Logger logger = LoggerFactory.getLogger(MyVaadinServlet.class);
#Override
protected void servletInitialized() throws ServletException {
getService().addSessionInitListener(this::onServletInit);
super.servletInitialized();
}
private void onServletInit(SessionInitEvent sessionInitEvent) {
SecurityContext securityContextOwnedByFilter = SecurityContextHolder.getContext();
VaadinSession session = sessionInitEvent.getSession();
User user = (User) securityContextOwnedByFilter.getAuthentication().getPrincipal();
session.setAttribute("user", user);
logger.info("User '{}' stored in session '{}'",
user.getUsername(),
session.getSession().getId());
}
}
Related
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 in a real problem here and would require your help. I work for a bank and have been assigned a task of implementing an OAuth2 service using Spring Boot, I have been exploring since last one week and have been able to implement a password flow grant type OAuth2 service, but now I have a few questions as my seniors said that password flow is not the right fit for our use case. First I would like to explain the use case:
Step 1: user will hit the application URL of the web app where no login is needed and before the application loads the OAuth2 service will be hit with logged in AD (system) user id.
Step 2. The OAuth2 service should authenticate the user using ldap with the given user id and return back all groups the user is part of along with the access token which will be used to access the API's there after
Now I have the below queries:
Which grant type is best suited for my need, from what I have read authorization code grant type seems to be the right fit? Or is it implicit?
Depending on the answer for question 1 what code changes do I need to make in the below code:
Code snippet for my authorization server:
Oauth2AuthserverApplication.java
#SpringBootApplication
#EnableAuthorizationServer
public class Oauth2AuthserverApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2AuthserverApplication.class, args);
}
}
OAuth2Congig.java
#Configuration
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
private String clientId = "client";
private String clientSecret = "secret";
private String privateKey = "private-key";
private String publicKey = "public-key";
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Bean
public JwtAccessTokenConverter tokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
converter.setVerifierKey(publicKey);
return converter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(tokenEnhancer());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authenticationManager(authenticationManager).
tokenStore(tokenStore())
.accessTokenConverter(tokenEnhancer());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.tokenKeyAccess("permitAll()").
checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws
Exception {
clients.inMemory().withClient(clientId).
secret(clientSecret).scopes("read", "write")
.authorizedGrantTypes("password",
"refresh_token").accessTokenValiditySeconds(20000)
.refreshTokenValiditySeconds(20000);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomDetailsService customDetailsService;
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws
Exception {
auth.userDetailsService(customDetailsService).
passwordEncoder(encoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().
and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
}
Not pasting the model, dao, and service code of the authorization server class as they are not relevant for this question.
Code Snippet from resource server project:
OAuth2ResourceserverApplication.java
#SpringBootApplication
#EnableResourceServer
#RestController
public class Oauth2ResourceserverApplication {
public static void main(String[] args) {
SpringApplication.run(Oauth2ResourceserverApplication.class, args);
}
#RequestMapping(value="/api")
public String success() {
return "SUCCESS";
}
}
JwtConverter.java
#Component
public class JwtConverter extends DefaultAccessTokenConverter implements
JwtAccessTokenConverterConfigurer {
#Override
public void configure(JwtAccessTokenConverter converter) {
converter.setAccessTokenConverter(this);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().
and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER);
}
}
application.yml
server:
port: 8081
security:
oauth2:
resource:
filter-order: 3
jwt:
key-value: private-key
OAuth2 has 4 grant types. To quickly understand the difference between 'Resource Owner Password Credential', 'Authorization Code', 'Implicit', let us compare them side-by-side:
NOTE: The full explanation is available at: https://blog.oauth.io/oauth2-flow-grant-types-in-pictures/.
To answer your question:
Which grant type is best suited for my need, from what I have read authorization code grant type seems to be the right fit? Or is it implicit?
If you compare based on 'Security' the purple bar, 'Authorization Code' is best. However you can see that it has the concept of Guard (backend) that performs access to the User data store on behalf of the App (frontend), i,.e., the App never has access to the Key/Access Token directly, as the Key is retrieved through the exchange of username/password between the User and the OAuth Server, and then passed on to the Guard.
The 'Resource Owner Password Credential' that you implemented is the least secure as the username/password is handed over to the App for the App to do everything that the User can without the User's further consent. However, in your scenario, the App and the User data store both belong to you so that alleviates the security issue.
Depending on the answer for question 1 what code changes do I need to make in the below code:
The full flow of Resource Owner Password Credential grant type you implemented is the left part of the image below and the Authorization Code grant type is the right part. As you can see, there are generally 5 steps. For Resource Owner Password Credential, some steps are not necessary, i.e., marked 'N.A.'.
NOTE:
The 'cloud' represents the App
The 'www' represents the User/Browser
The 'safe' represents the OAuth Server
To get from the left-side to the right-side, changes you required will be:
Step 1. If your OAuth Server is going to support different Apps, then it needs to support App pre-registration to get client id/secret. If you only have one App then you can skip this.
Step 2. Instead of the App prompting for username/password now the App will redirect the User to the OAuth Server to perform username/password authentication
Step 3. After authenticating the User the OAuth Server can prompt the User about what kind of permissions (e.g., read email, update profile, etc.) she wants to grant the App
Step 4. The OAuth Server instead of handing the Key/Access token to the App, it hands a code to User which then passed it to the App
Step 5. The App then exchanges the code for the Key/Access Token with the OAuth Server.
Once you get the Key/Access Token you can call any protected API on a different server which can then validate the Key/Access Token with the OAuth Server before responding to the API request, e.g., return the groups a User belongs to.
I have a REST service that uses OAuth2 authentication and that provides an endpoint to request a token with the client_credentials grant type. The application is based on Spring Boot.
So far I figured out I can request a token with something like:
#SpringBootApplication
#EnableOAuth2Client
public class App extends WebSecurityConfigurerAdapter {
#Autowired
OAuth2ClientContext oauth2ClientContext;
//...
#Override
protected void configure(HttpSecurity http) throws Exception {
// Does nothing - to allow unrestricted access
}
#Bean
protected OAuth2RestTemplate myTemplate() {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
details.setAccessTokenUri("http://localhost:8080/oauth/token");
details.setClientId("theClient");
details.setClientSecret("thePassword");
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
}
#RestController
public class TestController {
#Autowired
OAuth2RestTemplate myTemplate;
#RequestMapping("/token")
private String getToken() {
return myTemplate.getAccessToken().getValue();
}
}
And it almost works, but whenever I call the /token endpoint, there's an exception:
org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:88) ~[spring-security-oauth2-2.0.9.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.0.9.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.9.RELEASE.jar:na]
...
The exception is thrown here, but I'm not sure how I can make Spring use context authentication other than AnonymousAuthenticationToken. In fact, I don't want any authentication from the client, because anonymous is perfectly okay. How can I achieve this?
I have two different types of users.
SSO users
DB users.
SSO users would have already been authenticated by different system and DB users should be authenticated by our system. Can i configure Spring security to handle this scenario where by i can say prompt login page for some users and don't prompt for some.
Lets say for SSO users i can get there users ID in request headers while DB when they access the application there is no user id present in request header .How can i handle this scenario ?
Can i override authenticate method of DaoAuthenticationProvider by extending it and then page on some parameter decide to authenticate user or is there any other means ? can i add any information to Authentication class
This is What i have tried to Far
Security Config java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery("select username, role from user_roles where username=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic()
.and().addFilterBefore(new UserTypeFilter(), BasicAuthenticationFilter.class);
}
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
/*
* #Bean public MethodSecurityInterceptor methodSecurityService() { return
* new MethodSecurityInterceptor(); }
*/
#Bean(name="myAuthenticationManager")
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public ExceptionTranslationFilter exceptionTranslationFilter() {
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint());
AccessDeniedHandlerImpl accessDeniedHandlerImpl = new AccessDeniedHandlerImpl();
accessDeniedHandlerImpl.setErrorPage("/exception");
exceptionTranslationFilter
.setAccessDeniedHandler(accessDeniedHandlerImpl);
exceptionTranslationFilter.afterPropertiesSet();
return exceptionTranslationFilter;
}
#Bean
public UserTypeFilter authenticationFilter() throws Exception {
UserTypeFilter authFilter = new UserTypeFilter();
authFilter.setAuthenticationManager(authenticationManager());
return authFilter;
}
}
My Custom AbstractAuthenticationProcessingFilter
public class UserTypeFilter extends AbstractAuthenticationProcessingFilter {
private static final String INTERCEPTOR_PROCESS_URL = "/index";
#Autowired
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
public UserTypeFilter() {
super(INTERCEPTOR_PROCESS_URL);
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
System.out.println(" BASIC AUTHENTICATION FILTER");
String userId = request.getHeader("USERID");
if (userId == null) {
System.out.println(" THROWING EXCEPTION FILTER");
throw new PreAuthenticatedCredentialsNotFoundException("USERID param not found");
}
return null;
}
}
My Controller
#Controller
public class MainController {
#RequestMapping(value = { "/index" }, method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView model = new ModelAndView();
model.addObject("message", "This is test page!");
model.setViewName("dummy");
return model;
}
}
The control goes to My Custom filter and then when the exception is thrown but ExceptionTranslationFilter is not getting called
Have i configured httpsecurity correctly
Have i configured My custom filter correctly
Have i configured ExceptionTranslation Filter
Am i missing anything
This is a pretty standard use case for spring security. You will need to provide an Authentication object into the security context before any security interceptor is encountered.
Typically you would have some kind of filter which extracted SSO parameters from the request, authenticated those parameters against the SSO service, and then create an Authentication object and put it into the security context. The type of filter and configuration of the filter will depend on what SSO technology you are using.
There would often also be a filter (usually an ExceptionTranslationFilter) which will send unauthenticated requests to a login page.
There would also be filters to receive the parameters from the login form and store them in the security context.
Putting it all together I would expect one possible workflow to be:
User logs in with SSO parameters
Request comes in prepopulated with credentials
Some filter extracts those credentials, verifies them, creates an Authentication object, places the object in the security context.
The security interceptor finds the Authentication object in the security context, verifies the user is allowed access to the particular function, and passes the request on.
User logs in without SSO parameters (needs login page)
Request comes in with no credentials
The security interceptor finds no Authentication object and throws an exception.
The ExceptionTranslationFilter turns the exception into a redirect to a login page.
User logs in with filled out login form (e.g. for DB login)
Request comes in with a login form as the entity body
Some filter (e.g. UsernamePasswordAuthenticationFilter) extracts the credentials from the login form and defers to an authentication provider (e.g. your DAO authentication provider) to query the database and verify the user. If verified this filter will create an Authentication object and place it in the security context.
The security interceptor finds the Authentication object in the security context, verifies the user is allowed access to the particular function, and passes the request on.
I have a java that calls a Servlet:
public class UserServlet extends HttpServlet {
#Autowired
private UserService userService;
#Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
userService.checkUser();
userService.doSomethingRestricted();
}
#Override
public void init(final ServletConfig config) throws ServletException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext());
super.init(config);
}
}
And my autowired service :
#Component(value = "userService")
public class UserService {
public boolean checkUser() {
if (SecurityContextHolder.getContext().getAuthentication() != null) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal() != null && auth.getPrincipal() instanceof User) {
User springUser = (User) auth.getPrincipal();
if (springUser != null) {
LOG.debug("USER CONNECTED :: {}", springUser.getUsername());
}
}
} else {
LOG.debug("NO CONNECTED USER, CREATING ONE");
Collection<GrantedAuthority> authorities = getGrantedAuthorities();
org.springframework.security.core.userdetails.User springUser = new org.springframework.security.core.userdetails.User("user","password", true, true, true, true, authorities);
Authentication auth = new UsernamePasswordAuthenticationToken(springUser, "", authorities);
SecurityContext sc = new SecurityContextImpl();
sc.setAuthentication(auth);
SecurityContextHolder.setContext(sc);
}
return true;
}
#Secured({ "CONNECTED" })
public void doSomethingRestricted() {
LOG.debug("SOMETHING RESTRICTED HAS BEEN DONE!!");
}
}
When I test my application the first time, the Java client sends a POST to the server, the server would check the user and would not find a context: a new context would be created.
When I run the java client the subsequent times, I find an existing Context (the one created in the first call).
Obviously there's something missing because If the first user logs in successfully it does not mean any user can connect.
What am I missing ? At first I thought about using sessions for each Java client's instance (I dont have web browser clients so I need to set the session ids manually), but when is Spring supposed to get or set the session id in the http request ?
TL;DR :
What does SecurityContextHolder.getContext().getAuthentication() do in my example ?
It gets you the authentication details of the current login user , have you added
<bean id="httpSessionFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
to introduce login for web application , spring security is designed to work with POJO as well , you would need to add this filter in your mapping if you are doing it old way. If you are using http tags in applicationContext then it should work as it is.
</security:filter-chain-map>
Its been quite long since I have used spring security without the new http tags in applicatin Context . The spring security context comes with different filters , SecurityContextPersistenceFilter determines how the context is persisted.
"org.springframework.security.web.context.SecurityContextPersistenceFilter" is for persisting security context per session .
Spring security derived from its integration with acegi security which used to have "net.sf.acegisecurity.
ui.webapp.HttpSessionIntegrationFilter" filter for the same task
It is a filter , so spring can identify session based on sessionid.