I've followed this and this tutorial to create a skeleton of an Angular.JS application with a Java-based backend. Its code can be found here.
It has authentication - at the start Spring boot writes to the console a random password, which you can use to login to the started application. User name is user and the password is printed in the console at the start:
Now I want to have several user accounts with fixed passwords (it's OK, if they are hardcoded).
How can I do this in that application without breaking compatibility with AngularJS?
I suppose that I have to modify SecurityConfiguration in UiApplication and use something like
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
as explained here, but I'm not sure that it won't break AngularJS.
That's the solution:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
class SecurityConfiguration extends
WebSecurityConfigurerAdapter {
#Autowired
public void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1")
.password("password1")
.roles("ADMIN")
.and()
.withUser("user2")
.password("password2")
.roles("USER");
}
As long as you don't change the authentication scheme (i.e. BASIC, FORM etc.), AngularJS doesn't care about how you manage the user accounts in the backend. Of course if you would have used a fixed user and password and have it hardcoded in the AngularJS code, you would have to change this.
It wouldn't break the angular compatibility. But in addition to the code change you mentioned, you would also need to make sure that you are allowing access to /login URI anonymously. Change in your overloaded configure method would also be required, something as follows:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/login.html").permitAll()
.antMatchers("/index.html", "/home.html").authenticated()
.antMatchers("/index.html", "/home.html").hasRole("USER")
.anyRequest().authenticated();
}
Related
I've been trying to make an application with Spring Boot that uses Google as an identity provider for logging in.
The following configuration (using Spring Security) seems to work for this:
#Configuration
#EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**","/callback/", "/error**")
.permitAll()
.anyRequest()
.authenticated();
}
}
However, I only really want to secure a small part of the website (e.g. everything under the /secured/ path).
So I thought changing the antMatcher that requests are authorized for would work for this:
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
antMatcher("/secured**")
.authorizeRequests()
...
When I try this the application still redirects me to /login, but it gives me a 404 on that page instead of redirecting me to google's servers.
I could of course add all public urls to the permitAll antMatcher, but that seems cumbersome to do everytime a new one is added.
Putting all public things on a /public** path is also not something I'd like to do since it'd look weird in the url
Could anybody shed some light on what is happening here or maybe offer alternative solutions?
Thanks in advance!
I'm trying to use spring security to secure a rest/stateless api using JWT tokens. From the research I've been seeing, it involves turning off the spring security session management and then adding some custom filters to handle the user logging in as well as checking for the jwt token.
The problem I'm having is that once i add a filter, it's run on every instead of just the endpoints I want it on. I need to open up the login endpoint as well as a few others that will facilitate enrollment and reference data that doesn't need to be secured.
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/user").permitAll()
.anyRequest().authenticated().and()
.addFilterBefore(new StatelessAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
;
}
}
All StatelessAuthenticationFilter does is print "in here". I'm only expecting to see that message print when you go to localhost:8080/api/order, but i see it show up when you go to localhost:8080/api/user.
Is there a way to get this behavior?
The way you configured, the HttpSecurity will be applied to all the URLs including the user endpoint.
authorizeRequests() .antMatchers("/api/user").permitAll() line won't prevent "user" endpoint from authentication filter being called.
It just says that any authenticated user can call it.
You need to apply the filter to "order" endpoint only. Like this:
http .requestMatchers().antMatchers("/api/user") .and() .authorizeRequests().
#tsolakp's answer sorta works for me. I ended up overriding the
configure(Websecurity) method though
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/user");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated().and()
.addFilterBefore(new StatelessAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
;
}
I have below configuration class where I would like to authorize certain requests and deny all others.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/phx-config-rest/dev/master").hasRole("DEV")
.anyRequest().authenticated()
.and()
.csrf()
.disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
inMemoryAuthentication()
.withUser("devuser")
.password("dev")
.roles("DEV");
}
}
As per this code my impression was, Spring will only allow me to access /phx-config-rest/dev/master using the user 'devuser' and If I try access /phx-config-rest/prod/master or any other url, request would considered as un-authorized access. BTW, this code piece is regarding Spring cloud config server. Any thought?
change the
.anyRequest().authenticated()
to
.anyRequest().denyAll()
You restrict only URL /phx-config-rest/dev/master to users with role DEV, but all other URLs are accessible for every logged in user (including user devuser) with any role,
see ExpressionUrlAuthorizationConfigurer.AuthorizedUrl#authenticated:
Specify that URLs are allowed by any authenticated user.
You have to use ExpressionUrlAuthorizationConfigurer.AuthorizedUrl#denyAll instead of authenticated:
Specify that URLs are not allowed by anyone.
I am having a difficult time figuring out how to wire up spring security without forcing authentication. My specific app does not require the user to authenticate but the user can authenticate if they want.
I currently have a WebSecurityConfigurerAdapter set up that you can see at the end of this post. With this setup I'm getting a 403 on all /api/* requests and /j_spring_security_check.
Could someone please help me either fix my existing config or point me to a working example that accomplishes this?
Every example I see out there seems to require the user to authenticate and if they don't it throws a 403. In my app, I'm just using it to establish a session and at this point all users should be able to access all end points regardless of if they are authenticated or not.
WebSecurityConfig
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ItAuthenticationProvider customAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/**").permitAll()
.antMatchers("/j_spring_security_check").permitAll()
.and().formLogin()
.loginProcessingUrl("/j_spring_security_check")
.defaultSuccessUrl("/successful.html")
.loginPage("/#login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/successful.html");
}
}
You can take a look at this sample Spring Security application we built with Stormpath support. In this example, the home screen does not require authentication. Furthermore, the information that is displayed there is dynamically calculated based on whether the user is authenticated or not.
If I understand your requirement, you can use the anonymous authentication.
The documentation can be found here
I am trying to implement Spring Security to my MVC Maven based project with already included Spring Boot.
I have working front-end and back-end but until now I was using fake login - I was simply scoping user data such as username and password via JS and AngularJS and sending it to the back-end controller where data from DB was retrieved via DAO layer and compared to the scoped info after witch response was sent - and if it was "OK" I forwarded user to the User Home Page and vice versa.
This means that if I run my app and type directly to the browser bar localhost:8080/#/user (page that only user that is loged in is supposed to see) I can access it without any problems.
Now when business logic layer testing is finished the time has come to implement Spring Security.
I am still trying to understand Spring Security and trying to properly write SecurityConfiguration.java file - to have it set so it wont let no one access book.html and user.html pages, and, eventually, redirect such user to login.html.
This is my SecurityConfiguration.java file:
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Resource
private DataSource dataSource;
#Bean
public BCryptPasswordEncoder PasseordEncoder() {
return new BCryptPasswordEncoder();
}
protected void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
userDetailsService.setDataSource(dataSource);
PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.jdbcAuthentication().dataSource(dataSource);
if (!userDetailsService.userExists("user")) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("USER"));
User userDetails = new User("user", encoder.encode("password"),
authorities);
userDetailsService.createUser(userDetails);
}
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user").access("hasRole('USER')")
.antMatchers("/book").access("hasRole('USER')");
}
}
But when I run the app and try to access for example /user it allows me to! Can someone help me understand what am I to do, and how to fix this and pass the first step?
I think you forgot to declare an entry point for authentication. As you noted in your comment, you need to add .and().formLogin().loginPage("collections/login") to your http.authorizeRequests().
But, you also need to authorize unauthenticated access to the login page to avoid the redirect loop : ask page -> need authentication -> redirect to login page -> need authentication -> redirect ...
You should simply have followed the following example of the reference manual about Java Configuration and Form Login :
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
With your code, it gives :
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/user").access("hasRole('USER')")
.antMatchers("/book").access("hasRole('USER')")
.and().formLogin().loginPage("/collections/login").permitAll();
}
Last remark : note the / in front of login URL. Always use absolute path if you want your login page to be found from any page other than / !
Give this a go
Current
http.authorizeRequests()
.antMatchers("/user").access("hasRole('USER')")
.antMatchers("/book").access("hasRole('USER')");
Change to:
http.authorizeRequests()
.antMatchers("/user").hasRole('USER')
.antMatchers("/book").hasRole('USER');
Hope that helps (worked for me).