I have a question about storing "base" password for spring security app. I read documentation and IMHO i should have first pass stored somewhere hardcoded. Is that right or how i should be done?
As example i've post defauld helloWorld code from spring security.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
#Override
public UserDetailsService userDetailsService() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
String s = encoder.encode("password");
UserDetails user = User.withUsername("userName")
.password(s)
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
You have several options.
Hardcode it.
Store username/pass in a .property file. This gives you more flexibility, and also an ability to disable the user (e.g. set the name to empty and skipping it in the code)
Generate password via existing PasswordGenerator and add login/pass manually to a DB via SQL. This way you have even more flexibility, you can have as many initial users as you want (and you can always delete them), and also this approach can guarantee that logins will be unique (you need some special handling in your code to check, that the login of any new user doesn't match the login of the first user).
Related
Struggling with new Spring Security 6.x. Having the following SecuritsFilterChain:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity
public class SecurityConfiguration {
public SecurityConfiguration() {}
#Bean
#Order(1)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/swagger-ui/**", "/swagger-ui/index.html**", "/swagger-ui/index.html/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterAfter(new JWTAuthorizationFilter(), BasicAuthenticationFilter.class));
return http.build();
}
Unfortunately the URL:
/swagger-ui/index.html
executes the filter JWTAuthorizationFilter which should not be executed.
Without the filter it works.
First, allow me to suggest a few changes for readability, and then I'll comment after that:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(...).permitAll()
.anyRequest().authenticated()
)
.addFilterAfter(new JWTAuthorizationFilter(), BasicAuthenticationFilter.class)
return http.build();
}
The nice thing about this is that it's a little easier to see the authorization rules separately from the rest of the filters being configured.
permitAll
Authentication filters are called on every request.
permitAll(), authenticated(), and hasRole() all work with AuthorizationFilter, which is placed after the authentication filters in the filter chain.
What this means is that JWTAuthorizationFilter, BasicAuthenticationFilter, and all authentication filters are not affected by any authorization rules that you add.
If there are certain requests that JWTAuthorizationFilter should skip, that is up to that filter to say so.
I want to secure my vaadin application with keycloak and spring security. I try to use the "keycloak-spring-security-adapter".
My problem is that I want also unauthenticated users to use my application, but with less functionality - I do this with method security and checking which roles the current user has in the UI.
Can I configure the filter so that it ignores unauthenticated requests, but If the token is present uses it?
Thanks
Daniel
A working example of what you want can be found in the public-access branch of this github project. It does use Vaadin 8 though.
In essence, you can setup your application to be partially public, i.e. accessibly to unauthenticated user for certain parts and requires login for others, as follows:
#Configuration
#EnableWebSecurity
#EnableVaadinSharedSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.formLogin().disable();
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/vaadinServlet/UIDL/**").permitAll()
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll()
.anyRequest().permitAll();
http
.logout()
.addLogoutHandler(keycloakLogoutHandler())
.logoutUrl("/sso/logout").permitAll()
.logoutSuccessUrl("/");
http
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class);
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
http
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy());
}
...
}
The line http.anyRequest().permitAll(); is the most important where you configure the filter to just allow all requests. You could still update this to only allow public access to certain urls.
You can then use spring security annotations on methods/views/components to configure your fine-grained access control. E.g:
#SpringComponent
#Secured("ROLE_ANONYMOUS")
public class LoginOperation implements Runnable {
#Override
public void run() {
// login logic
}
}
and
#Secured("ROLE_USER")
public class LogoutOperation implements Runnable {
#Override
public void run() {
// logout logic
}
}
Spring Security 4 enables developers to write less code, but sometimes this also causes thing easily to get out of control. For example, now I am writing a login function, once the user pressed a button (login/unionauth URI), an OAuth 2.0 like authentication offered by a 3rd party would be launched, and finally the result comes back and we compare the user with our local database. In order to do so, first I have adapter class like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
.and()
.logout()
.logoutSuccessUrl("/").permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
Then I have ssoFilter() like this:
public class UnionAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
// authentication steps.
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("tom", "N/A", null);
AuthenticationManager manager = this.getAuthenticationManager();
return manager.authenticate(token);
}
Now, the problem is, manager is null. Why it is null? I think in case of password username mode, the auth.userDetailsService(myUserDetailsService); in adapter would enable a DAO manager. In official website, it has this:
How to Add a Local User Database
Many applications need to hold data about their users locally, even if
authentication is delegated to an external provider. We don’t show the
code here, but it is easy to do in two steps.
Choose a backend for your database, and set up some repositories (e.g.
using Spring Data) for a custom User object that suits your needs and
can be populated, fully or partially, from the external
authentication.
Provision a User object for each unique user that logs in by
inspecting the repository in your /user endpoint. If there is already
a user with the identity of the current Principal, it can be updated,
otherwise created.
Hint: add a field in the User object to link to a unique identifier in
the external provider (not the user’s name, but something that is
unique to the account in the external provider).
Any idea how to add database into Spring Security OAuth2 or why the manager in the first paragraph is null?
#SuppressWarnings("SpringJavaAutowiringInspection")
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder
authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService);
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/test").permitAll()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
}
I have a custom filter that runs before Spring Security. I want to be able to exclude some URLs (like /test) from the filter and Spring Security and others to be intercepted (like /api/**).
When using postman to test localhost/test it still goes through the filter even though I have antMatchers("/test").permitAll().
How do I bypass the filter?
You can disable the Spring Security filter chain for some URLs, see WebSecurity#ignoring:
Allows adding RequestMatcher instances that should that Spring Security should ignore. Web Security provided by Spring Security (including the SecurityContext) will not be available on HttpServletRequest that match. Typically the requests that are registered should be that of only static resources. For requests that are dynamic, consider mapping the request to allow all users instead.
Example Usage:
webSecurityBuilder.ignoring()
// ignore all URLs that start with /resources/ or /static/
.antMatchers("/resources/**", "/static/**");
Therefore, you can override WebSecurityConfigurerAdapter#configure:
Override this method to configure WebSecurity. For example, if you wish to ignore certain requests.
To ignore path /test you have to add following method to your configuration:
public void configure(WebSecurity web)
webSecurityBuilder
.ignoring()
.antMatchers("/test");
}
Swagger works! I can interact with http://localhost:8090/sdoc.jsp and everything is fine.
I add the following to pom.xml...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
I also add the following two files:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if( !Authenticate.authenticate(name, password) )
return null;
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
and
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().permitAll()
.antMatchers("/**").authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
.httpBasic()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider());
}
}
At this point if I visit the same URL that was previously working I now instead get a response type of "text/plain" and instead of a pretty HTML looking browser I see source code.
If I revert the change and remove the two files from project and remove JAR file it works again.
How do I get Spring Security and Swagger to play nice? What am I doing wrong.
I suspect this is due to Spring-Security's effect on the content-type headers (http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/headers.html#headers-content-type-options).
From the docs -
Historically browsers, including Internet Explorer, would try to guess the content type of a request using content sniffing. This allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type. For example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content type and then execute it.
The problem with content sniffing is that this allowed malicious users to use polyglots (i.e. a file that is valid as multiple content types) to execute XSS attacks. For example, some sites may allow users to submit a valid postscript document to a website and view it. A malicious user might create a postscript document that is also a valid JavaScript file and execute a XSS attack with it.
Again, from the docs, in order to override the default -
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.contentTypeOptions();
}
}
Wow, I figured it was something along these lines. Thanks so much
When I tried this and it started working
.headers()
.disable()
I narrowed the default contentTypeOptions down to..
.headers()
//.contentTypeOptions() // If this is uncommented it fails.
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.frameOptions()
.and()