this is my Java Config:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("Jon")
.password("123456")
.roles("USER")
.and()
.withUser("Bob")
.password("qwer")
.roles("USER", "ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET,"/admin/**").access("hasRole('ADMIN')")
.anyRequest().authenticated()
.and()
.httpBasic()
;
}
}
As you can see, I hava to peroson
Jon, has role as USER
Bob, has roles as USER ADMIN
But, so Jon should not hava permition to visis /admin/**, then I write follow test
ResultMatcher isUnauthorized = MockMvcResultMatchers.status()
.is(401);
String credential = Base64.getEncoder().encodeToString("Jon:123456".getBytes());
MockHttpServletRequestBuilder builder =
MockMvcRequestBuilders.get("/admin/")
.header("Authorization", "Basic " + credential);
mockMvc.perform(builder)
.andDo(MockMvcResultHandlers.print())
.andExpect(isUnauthorized);
I expect this test return 401 unauthorized, But actually it return 200.
What is the problem?
SpringSecurityVersion is 5.1.5.RELEASE
Related
There is a project with Spring boot back-end on running on localhost:8080 and 2 front-end angular applications on localhost:4200 (User website) and localhost:4201(Admin website).How can i configure spring security so that it allows only users with role - ROLE_USER,ROLE_ADMIN in User website and users with role -ROLE_ADMIN should have access to Admin Website.
Currently both users are able to access both wesbite.Is there any way to restrict certain domains rather restricting paths(URLs) to users.
Current config -
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
Environment env;
#Autowired
UserSecurityService useSecurityService;
private BCryptPasswordEncoder passwordEncoder() {
return SecurityUtility.passwordEncoder();
}
private static final String[] PUBLIC_MATHCES= {
"/css/**",
"/js/**",
"/images/**",
"/book/**",
"/user/**",
"/media/**"
};
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(useSecurityService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(PUBLIC_MATHCES).permitAll()
.anyRequest().authenticated()
.and();
http.csrf().disable()
.cors()
.and()
.httpBasic();
}
#Bean
public HttpSessionIdResolver httpSessionStrategy() {
return HeaderHttpSessionIdResolver.xAuthToken();
}
}
suppose all of your configurations configured properly, then you can make use of the role restriction mechanism as the below sample :
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize - > {
authorize
.antMatchers("/h2-console/**").permitAll() //do not use in production!
.antMatchers("/css/**", "/js/**", "/images/**", "/book/**", "/user/**", "/media/**").permitAll()
.antMatchers("/website/find", "/main*").permitAll()
.antMatchers(HttpMethod.GET, "/userweb/v1/data/**").permitAll()
.mvcMatchers(HttpMethod.DELETE, "/userweb/v1/info/**").hasRole("ADMIN")
.mvcMatchers(HttpMethod.GET, "/userweb/v1/item/{upc}").permitAll()
.mvcMatchers("/admin/main").hasAnyRole("USER", "ADMIN")
.mvcMatchers(HttpMethod.GET, "/user/api/v1/normal")
.hasAnyRole("USER", "ADMIN", "FOO");
})
.authorizeRequests()
.anyRequest().authenticated()
.and()
.cors()
.and()
.httpBasic()
.and().csrf().disable();
}
I added two filter like below
JwtUsernameAndPasswordAuthenticationFilter
JwtTokenVerifier
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/makestory").hasAnyRole("OLDBIE")
.and()
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter())
.addFilterAfter(new JwtTokenVerifier(), JwtUsernameAndPasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginErroHandler());
}
and but I want to exclude some of urls like /main/, /main/story/, etc
so I was expected that /main/, /main/story/ urls don't need to be censored
so I added below code
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/main/**");
}
but there's a problem. A request has been passed to JwtUsernameAndPasswordAuthenticationFilter class
public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException{
log.info("JwtUsernameAndPasswordAuthenticationFilter is actived");
}
}
I don't want to a request be passed into JwtUsernameAndPasswordAuthenticationFilter
what should I do?
Update:
_____ Only to bypass Security _____
You may add matchers and permit before asking authentication for any other request
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.
.
.
.antMatchers("/main/**")
.permitAll()
.anyRequest()
.authenticated()
.
.
}
this part may not be necessary only for bypass auth.
public void configure(WebSecurity web)
______ apply filter to only matching _______
#Bean
public FilterRegistrationBean<JwtUsernameAndPasswordAuthenticationFilter> loggingFilter() {
FilterRegistrationBean<JwtUsernameAndPasswordAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtUsernameAndPasswordAuthenticationFilter());
registrationBean.addUrlPatterns("/home**", "/test", "/sample/**");
return registrationBean;
}
I am going to set up background Spring Authentication by a custom class that implemented UserDetailsService. As said in the documentation method loadUserByUsername calls each time when user try to login, but it don't in the case below.
#Component
public class AuthenticationUserDetailsService implements UserDetailsService {
#Autowired
private PersonRepository personRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Person> personByUserName = personRepository.findPersonByUserName(username);
Person person = personByUserName.
orElseThrow(() -> new UsernameNotFoundException("User with username has not found"));
return new User(username, person.getPassword(), Collections.emptyList());
}
}
I try to use several alternatives to set up configure(HttpSecurity http) method in WebSecurityConfig class.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("api/person/login")
.usernameParameter("username")
.passwordParameter("password");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/", "/home", "/api/person/login").permitAll()
.anyRequest().authenticated();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.
authorizeRequests().
anyRequest().authenticated()
.and().
formLogin().
loginProcessingUrl("/login").
usernameParameter("username").
passwordParameter("password");
}
Registration DaoAuthenticationProvider:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(getAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(authenticationUserDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
Could anyone explain, where is error, and why loadUserByUsername method doesn't call at all?
The Angular 8 UI part uses to login.
public loginPerson(person: Person): Observable<Person> {
let url = "http://localhost:8080/login";
return this.httpClient.post<Person>(url, {userName: person.username, password: person.password});
}
I have two types of users: Application User and End User and I have separate tables for these. Now, I want to apply security on these two tables.
I provided custom implementation of UserDetailsService for Application users:
#Component("applicationUserDetailsService")
public class ApplicationUserDetailsService implements UserDetailsService {}
And, I provided another second custom implementation of UserDetailsService for End users:
#Component("endUserDetailsService")
public class EndUserDetailsService implements UserDetailsService {}
Now, in the following code snippet, I have registered two endpoints for both type of users. I have injected both implementation of UserDetailsService and registered by #Overide configure(AuthenticationManagerBuilder auth) method for both application and end user separately.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Import(SecurityProblemSupport.class)
public class SecurityConfiguration {
// Injected via Constructor Injection
private final EndUserDetailsService endUserDetailsService;
private final ApplicationUserDetailsService applicationUserDetailsService;
#Configuration
#Order(1)
public class ApplicationUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.antMatcher("/api/customer/**")
.authorizeRequests()
.antMatchers("/api/customer/authenticate").permitAll()
.antMatchers("/api/customer/**")
.authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(endUserDetailsService);
}
}
//no #Order defaults to last
#Configuration
public class EndUserSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf()
.disable()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(applicationUserDetailsService);
}
}
private JWTConfigurer securityConfigurerAdapter() {
return new JWTConfigurer(tokenProvider);
}
}
And, I'm trying to authenticate the user like this:
//Injected via Constructor Injection
private final AuthenticationManagerBuilder authenticationManagerBuilder;
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
When the above code snippet is executed, I get the Null Pointer Exception because authenticationManagerBuilder.getObject() returns NULL. And when I use just when implementation of UserDetailService with #Component("userDetailsService") and not set UserDetailService in security config like auth.userDetailsService("..."), it works fine but by that way I can't achieve authentication from multiple tables.
What I want to Achieve:
In simple words, I want spring security to authenticate user from two tables.
requestMatchers() is the call that you need as it allows you to isolate adapters by URL:
#Order(1)
#EnableWebSecurity
class EndUserConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/customer/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("CUSTOMER")
.and()
.apply(yourJointConfigurations());
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(endUserDetailsService);
}
}
Regarding calling the AuthenticationManager directly, it would be ideal if you could rely on the existing filter chain to do the work for you. For example, since you are stateless, HTTP Basic might be a better fit for you, which you could apply to both configurations, instead of trying to have a dedicated /authenticate endpoint.
The route /gateways needs authentication.
When /gateways is accessed in the browser I am redirected to /login and the following form appears:
If /gateways is accessed from an angular2 app the following popup appears:
My spring security configuration is the following:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static String REALM="Authentication";
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("cris").password("123").roles("ADMIN");
auth.inMemoryAuthentication().withUser("felix").password("felix123").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.formLogin()
.and()
.authorizeRequests()
.antMatchers("/user", "/vehicles", "/signin", "/isautheticated").permitAll().anyRequest()
.authenticated();
}
// Access-Control-Allow-Origin header to be present
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
/* To allow Pre-flight [OPTIONS] request from browser */
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
So how can the popup be disabled?
You should specify formLogin() instead of httpBasic() in your configuration. your configure method should look like this.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login");
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.formLogin()
.and()
.authorizeRequests()
.antMatchers("/user", "/vehicles", "/signin", "/isautheticated").permitAll().anyRequest()
.authenticated();
}
I think your request from angular2 is taking an invalid Authorization Basic header, it was handled by BasicAuthenticationFilter, and it threw a AuthenticationException, and start to entry point.
You can implement your own entry point that implements AuthenticationEntryPoint, and then inject to BasicFilter, the default entry point is BasicAuthenticationEntryPoint. As you can see, it will return a WWW-Authenticate response header.
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
}