Spring Security method level security with Java config + REST Authentification? - java

I'm trying to add an authentification to my RestController, but i can't find any good documentation or any sample with Java Configuration.
I tried this but it doesn't work (i can access to all request without login)
My controller is annotated with #PreAuthorize
#RestController
#RequestMapping("/api/hello")
public class HelloController {
#RequestMapping(value = "/say", method = RequestMethod.GET)
public String sayHello() {
return "hello";
}
#PreAuthorize("hasRole('ROLE_USER')")
#RequestMapping(value = "/say/user", method = RequestMethod.GET)
public String sayHelloWithUserProtection(){
return "Hello USER";
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/say/admin", method = RequestMethod.GET)
public String sayHelloWithAdminrProtection(){
return "Hello ADMIN";
}
}
SecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackages = {"com.test.server.security"})
public class SecurityConfig {
#Autowired
public void configureAuthentification(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("admin").roles("USER","ADMIN");
}
#Configuration
public static class ApiWebConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.formLogin();
}
}
}
SecurityWebApplicationInitializer
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
How can i make it work?
And there is any good tutorial to make a REST token based (token which save session key and others custom values) authentification saved in database with JPA (or JDBC ?) with Java configuration ?

Drop the formLogin(). You need to maintain the mindset that REST is supposed to be stateless. Logging in with a form this way, is not purely REST.
You can create a fine masked filter with Spring security chains like this (randomly just added stuff to create a more complete. Spring Security works by filters, which means that you need to create an actual filter before it kicks in. Specifically you need to authorize requests before matching them to paths.
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/say/user/").hasRole("USER")
.antMatchers("/say/admin").hasRole("ADMIN")
.anyRequest().authenticated();
The code above should be self explaining. If not, I will try to elaborate on it.
As for token based login, this is a good idea, but you should not roll your own.
Spring has great Oauth support and getting started securing your REST API with it is awesome.
This tutorial explains it in great detail and should help you further in building better API's as well.
http://spring.io/guides/tutorials/bookmarks/
Also make sure you have a look at Fowler's writings on REST here
http://martinfowler.com/articles/richardsonMaturityModel.html

I forgot to put my WebApplicationInitialisation in the question.
My error was i put SecurityConfig in getRootConfigClasses() instead of getServletConfigClasses().
Now WebApplicationInitialisation looks like this and it work great !
public class WebApplicationInitialisation extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebMvcConfig.class, SecurityConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
}
}

Related

Spring security permitall() works for #RestController but not for #Controller

In my application, I have few APIs that I want to allow without authentication. So I added that patterns in the permitall(). But this only works if those patterns are inside the #RestController annotation. If those patterns are in #Controller annotation (I want to return views), Spring asks for the authentication even though they are under permitall().
WebsecutiryConfig class
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().
.authorizeRequests().antMatchers("/pattern1", "/pattern2").permitAll()
.anyRequest().authenticated()
}
Class with #RestController annotation
#RestController
public class RESTClass {
#GetMapping("/pattern1")
public String hello() {
return "my response";
}
Class with #Controller annotation
#Controller
public class ControllerClass {
#GetMapping("/pattern2")
public String hello(Model model) {
return "my view";
}
So how can I allow users to see those views without authentication?
So I figured it out. What was happening was that spring was allowing the view file to load without the authentication but it was not allowing to load the related css and js files. So I had to add them to the permitall() pattern.

Use multiple HttpSessionIdResolver with Spring

I want to use the HTTPSessionIdResolver for everything located under "/api**" and for everything else the standard CookieResolver.
How is this possible, so that the two configurations use different resolvers? With my current approach everything uses X-AUTH.
I tried to understand the implementation within Spring and I end up in the SessionRepositoryFilter, but of this filter only one instance is created, so der exists only one resolver.
#EnableWebSecurity
public class TestConfig {
#EnableSpringHttpSession
#Configuration
#Order(1)
public static class Abc extends WebSecurityConfigurerAdapter {
#Bean
#Primary
public HeaderHttpSessionIdResolver xAuth() {
return HeaderHttpSessionIdResolver.xAuthToken();
}
#Bean
#Primary
public MapSessionRepository mapSessionRepository(){
return new MapSessionRepository(new HashMap<>());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/service/json/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
}
#EnableSpringHttpSession
#Configuration
#Order(2)
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.build();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/user/registration", "/webfonts/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
public BCryptPasswordEncoder bcrypt() {
return new BCryptPasswordEncoder();
}
#Bean
public JdbcUserDetailsManager userDetailsManager() {
JdbcUserDetailsManager manager = new UserDetailsManager(dataSource());
manager.setUsersByUsernameQuery("select username,password,enabled from users where username=?");
manager.setAuthoritiesByUsernameQuery("select username,authority from authorities where username = ?");
return manager;
}
#Autowired
public void initialize(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsManager()).passwordEncoder(bcrypt());
}
}
}
I could move the logic into one resolver which delegates the work to the existing resolvers, but this seems hacky?
public class SmartHttpSessionIdResolver implements HttpSessionIdResolver {
private static final String HEADER_X_AUTH_TOKEN = "X-Auth-Token";
private static final CookieHttpSessionIdResolver cookie = new CookieHttpSessionIdResolver();
private static final HeaderHttpSessionIdResolver xauth = HeaderHttpSessionIdResolver.xAuthToken();
#Override
public List<String> resolveSessionIds(HttpServletRequest request) {
if (isXAuth(request)) {
return xauth.resolveSessionIds(request);
}
return cookie.resolveSessionIds(request);
}
#Override
public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
if (isXAuth(request)) {
xauth.setSessionId(request, response, sessionId);
} else {
cookie.setSessionId(request, response, sessionId);
}
}
#Override
public void expireSession(HttpServletRequest request, HttpServletResponse response) {
if (isXAuth(request)) {
xauth.expireSession(request, response);
} else {
cookie.expireSession(request, response);
}
}
private boolean isXAuth(HttpServletRequest request) {
return request.getHeader(HEADER_X_AUTH_TOKEN) != null;
}
}
As you mention above, based on the code of "SessionRepositoryFilter" class, it is clear that it supports only a single "HttpSessionIdResolver". As a result, if you use only one "SessionRepositoryFilter", your only option seems to be the one that you suggested. Although this would work, it does feel a bit hacky and also, if your requirement is indeed to use the "HTTPSessionIdResolver" for everything located under "/api**", then it doesn't ensure that.
Since "SessionRepositoryFilter" class is effectivelly a Filter, I suggest checking if you could create two Spring beans for the "SessionRepositoryFilter" class. One that will be used for all HTTP endpoints under the "/api*" pattern and another for all other paths. Then, you could use the "HttpSessionIdResolver" and "CookieSessionIdResolver" respectivelly. You can find an example for defining different filters based on URL patterns here.
After attempting the solution provided in the question (which works fine, to be honest), I also attempted to do this by providing two different filters. However, when #EnableSpringHttpSession is added, a SessionRepositoryFilter is added automatically and adding two more of those in the servlet filter chain seems odd. Therefore, I thought they would have to go in the security filter chain instead, which is good because then we can use the URL matching made there as well (instead of having to implement that elsewhere as well).
Since other security filters use the HttpSession, we have to manually place the SessionRepositoryFilter first in this chain. This is what I came up with (in Kotlin) which works well for me:
#EnableWebSecurity
class SecurityConfig() {
private val sessionStore = ConcurrentHashMap<String, Session>()
private val sessionRepo = MapSessionRepository(sessionStore)
#Configuration
#Order(1)
inner class XAuthConfig(): WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.requestMatchers()
.antMatchers("/api**")
.and()
.addFilterBefore(
SessionRepositoryFilter(sessionRepo).apply{
setHttpSessionIdResolver(
HeaderHttpSessionIdResolver.xAuthToken();
)
}, WebAsyncManagerIntegrationFilter::class.java)
}
}
#Configuration
#Order(2)
inner class DefaultConfig(): WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http
.addFilterBefore(
SessionRepositoryFilter(sessionRepo).apply{
setHttpSessionIdResolver(
CookieHttpSessionIdResolver()
)
}, WebAsyncManagerIntegrationFilter::class.java)
}
}
}
}
Note that the annotation #EnableSpringHttpSession has been removed. Instead, we add the SessionRepositoryFilters manually before the WebAsyncManagerIntegrationFilters (the first filter in the security filter chain). The function of the SessionRepositoryFilter is to replace the existing HttpSession with Spring's HttpSession which it will do no matter if we place it manually or if it's put in place automatically by means of autoconfiguration. As long as no other filter before the security filter chain makes use of the session, this should work out. Otherwise, some filter rearrangement might still do the trick.
I made it to work by creating a subclass of RedisHttpSessionConfiguration. Then you will have available a filter called springHeaderSessionRepositoryFilter which you can use to configure another filter with a different filter-mapping in your web app.
public class MyCustomRedisHttpSessionConfiguration extends RedisHttpSessionConfiguration {
#Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springHeaderSessionRepositoryFilter(SessionRepository<S> sessionRepository) {
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(HeaderHttpSessionIdResolver.xAuthToken());
return sessionRepositoryFilter;
}
}

Spring Security 5 / Login / REST API Accessibility / Bad Credentials even if they are right?

I am implementing a REST API using Spring Boot (2.0.1) to work with MongoDB (3.6). I'm really stuck. I've also tried other tips from StackOverFlow but it didn't help for some reason.
I have configured the SecurityConfig.java to permit the access to certain areas and also created a User inMemoryAuthentication, to be able to login to HAL Browser (Spring) and etc. But the problem is, that whatever address I put in browser I get a Login form and the credentials used in the inMemoryAuthentication is always wrong for some reason. The only way I've found to access the API is by excluding SecurityAutoConfiguration in the main class. But this opens up every permission to access everything including HAL Browser without authentication.
Would someone show me what I am doing wrong? I want to permit only certain paths/addresses to everyone, permit everything else only to use with TokenAuthentication (have already a custom implementation of it) and have one user (username, password) to access HAL Browser.
Here is my SecurityConfig.java:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
private final TokenAuthenticationService tokenAuthenticationService;
#Autowired
protected SecurityConfig(final TokenAuthenticationService tokenAuthenticationService) {
super();
this.tokenAuthenticationService = tokenAuthenticationService;
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterBefore(new TokenAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/hello").permitAll()
.antMatchers("/test2").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/useraccount").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth
// .inMemoryAuthentication()
// .withUser("user1").password("password").roles("ADMIN");
auth
.inMemoryAuthentication()
.withUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER"));
// auth
// .userDetailsService(userService);
}
// #Bean
// #Override
// public UserDetailsService userDetailsService() {
// UserDetails user =
// User.withDefaultPasswordEncoder()
// .username("user")
// .password("password")
// .roles("USER")
// .build();
//
// return new InMemoryUserDetailsManager(user);
// }
// #Bean
// public UserDetailsService userDetailsService() {
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// manager.createUser(User.withUsername("user").password("pass").roles("USER", "ADMIN").build());
// return manager;
// }
}
I've tried different approaches as you see (commented blocks) but still no luck.
Even though I have permitAll() on /register, i still get the auto generated login form, which won't accept any credentials.
So as i've said earlier the only way to use my API is to exclude the SecurityAutoConfiguration (#EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class) but it is not a secure option.
Is there any way to resolve this?
From what I can see, it's likely that your SecurityConfig class never gets called, as it doesn't have any annotation indicating to Spring Boot that it should look for beans to autowire in the class (#Autowired)
To give you an idea, the following will never be called:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
System.out.println("This will never called");
}
}
Whereas, if we had #EnableWebSecurity:
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
System.out.println("This is called");
}
}
Keep in mind that Spring Boot will not detect annotations inside a class if the class itself is not annotated with #Component or with another annotation that inherits the #Component annotation (such as #Configuration, #Service, ...)
EDIT: I quickly put together a program to imitate your situation:
SecurityConfiguration.java:
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser(new User("root", "root", Arrays.asList(new SimpleGrantedAuthority("USER"))))
.passwordEncoder(fakePasswordEncoder());
}
#Bean
public PasswordEncoder fakePasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return null; // matches(...) will always return true anyways
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/hello").permitAll()
.antMatchers("/test2").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/useraccount").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).permitAll();
}
}
Note that I just quickly made a password encoder that ignores the password because that would require more work
ExampleController.java:
#RestController
public class ExampleController {
#GetMapping("/")
public Object index() {
return getCurrentUser();
}
public Object getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return ((UsernamePasswordAuthenticationToken)auth).getPrincipal();
}
}
And when I login with the username root and the any password (remember, the fake password encoder doesn't care about the password), it redirects me to / and displays the following:
{"password":null,"username":"root","authorities":[{"authority":"USER"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true}
(which is normal because that's what I'm making it output)

401 instead of 403 with Spring Boot 2

With Spring Boot 1.5.6.RELEASE I was able to send HTTP Status code 401 instead of 403 as described in How let spring security response unauthorized(http 401 code) if requesting uri without authentication, by doing this:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//...
http.exceptionHandling()
.authenticationEntryPoint(new Http401AuthenticationEntryPoint("myHeader"));
//...
}
}
using the org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint class.
I just upgraded to Spring Boot 2.0.0.RELEASE and found there is not such class any more (at least in that package).
Questions:
Does this class (Http401AuthenticationEntryPoint) exist yet in Spring Boot?
If no, what could be a good alternative for keeping the same behavior in an existing project in order to keep consistency with other implementations which depend on this status code (401) instead of 403?
Please notice this is different from Spring Security anonymous 401 instead of 403 because it's referring specifically to SpringBoot 2 (there are solutions in that post not applicable anymore in SpringBoot version 2 or others are not needed at all)
Heads up
By default Spring Boot 2 will return 401 when spring-boot-starter-security is added as a dependency and an unauthorized request is performed.
This may change if you place some custom configurations to modify the security mechanism behavior. If that's the case and you truly need to force the 401 status, then read the below original post.
Original Post
The class org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint was removed in favor of org.springframework.security.web.authentication.HttpStatusEntryPoint.
In my case the code would go like this:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//...
http.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
//...
}
}
Bonus
If you need to return some information in the response body or customize the response somehow you can do something like this:
1- Extend AuthenticationEntryPoint
public class MyEntryPoint implements AuthenticationEntryPoint {
private final HttpStatus httpStatus;
private final Object responseBody;
public MyEntryPoint(HttpStatus httpStatus, Object responseBody) {
Assert.notNull(httpStatus, "httpStatus cannot be null");
Assert.notNull(responseBody, "responseBody cannot be null");
this.httpStatus = httpStatus;
this.responseBody = responseBody;
}
#Override
public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.setStatus(httpStatus.value());
try (PrintWriter writer = response.getWriter()) {
writer.print(new ObjectMapper().writeValueAsString(responseBody));
}
}
}
2- Provide an instance of MyEntryPoint to the security configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// customize your response body as needed
Map<String, String> responseBody = new HashMap<>();
responseBody.put("error", "unauthorized");
//...
http.exceptionHandling()
.authenticationEntryPoint(new MyEntryPoint(HttpStatus.UNAUTHORIZED, responseBody));
//...
}
}
Just to elaborate #lealceldeiro's answer:
Before Spring Boot 2 my Securiy Configuration class looked like this:
#Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {
#Bean
public Http401AuthenticationEntryPoint securityException401EntryPoint() {
return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
}
#Autowired
private Http401AuthenticationEntryPoint authEntrypoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
// some http configuration ...
// Spring Boot 1.5.x style
http.exceptionHandling().authenticationEntryPoint(authEntrypoint);
}
//...
}
And now in Spring Boot 2 it looks like this:
#Configuration
public class MyConfig extends WebSecurityConfigurerAdapter {
//Bean configuration for Http401AuthenticationEntryPoint can be removed
//Autowiring also removed
#Override
protected void configure(HttpSecurity http) throws Exception {
// some http configuration ...
// Spring Boot 2 style
http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
}
//...
}
See also this comment in Spring Boot Github Repo > PR Remove Http401AuthenticationEntryPoint.
Http401AuthenticationEntryPoint was removed.
See Spring Boot Github Repo > Issue #10715 (Remove Http401AuthenticationEntryPoint):
Remove Http401AuthenticationEntryPoint
rwinch commented on 20 Oct 2017
As far as I can tell it is not being used in the Spring Boot code base, so it might be good to remove Http401AuthenticationEntryPoint.
Depending on your requirements, you could use:
HttpStatusEntryPoint
BasicAuthenticationEntryPoint
For reactive (WebFlux) stack you can override the returned status code by adding such #Bean to catch some specific exceptions:
#Component
class MyErrorAttributes : DefaultErrorAttributes() {
override fun getErrorAttributes(
request: ServerRequest,
options: ErrorAttributeOptions
): MutableMap<String, Any> {
val cause = super.getError(request)
val errorAttributes = super.getErrorAttributes(request, options)
when (cause) {
is TokenExpiredException -> {
errorAttributes["status"] = HttpStatus.UNAUTHORIZED.value()
errorAttributes["error"] = HttpStatus.UNAUTHORIZED.reasonPhrase
}
}
return errorAttributes
}
}
You can customize your logic with overriding the class AuthenticationEntryPoint
this should be working:
#Component public class AuthEntryPointException implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setStatus(HttpStatus.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{\"result\":\"UNAUTHORIZED\",\"message\":\"UNAUTHORIZED or Invalid Token\"}");
}
}

Spring security 4. JSON REST with custom authentication and authorization

We're developing Spring 4 REST/JSON API but we need to have custom Authentication service to authenticate against 3rd party service.
restrictions: We don't want to ask the user for username/password. we are authenticating using "cookie" (send with the request initiator). And we need this authentication process in background. (Might sound weird but that's the case).
we could implement that using registering custom authentication/authorization request filters. but that made us loosing the power of spring "authorization" module that we are planning to use afterwards.
So what we did till now, wrote custom WebSecurityConfigurerAdapter with our own custom AuthenticationProvider and UserDetailsService but these configuration doesn't seem to work.
the application doesn't go inside AuthenticationProvider.authenticate
here is the configuration we had.
AuthenticationProvider.java:
#Service
public class AuthenticationService implements AuthenticationProvider, UserDetailsService {
#Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
// DOESN'T ENTER THIS FUNCTION
// do authentication stuff
}
#Override
public boolean supports(Class<?> authentication) {
// JUST FOR TESTING
return true;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// DOESN'T ENTER THIS METHOD
return null;
}
}
SecurityConfig.java:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);
#Autowired
private AuthenticationService authService;
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/ignoredURL/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //HTTP with Disable CSRF
.authorizeRequests()
.antMatchers("/api/XYZ/**").hasRole("ADMIN")
.anyRequest().authenticated();
// adding ".httpBasic()" automatically prompts user for username/password
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// THIS IS NOT TYPO, I use one service implements both interfaces.
auth.userDetailsService(authService);
auth.authenticationProvider(authService);
}
}
Fixed by adding 2 more classes (Filter extending AbstractPreAuthenticatedProcessingFilter and another class CustomUser implements UserDetails) then made my AuthenticaitonService implements spring UserDetailsService here are the details:
please have a look on what this filter does and how it works
1- Create AbcFilter extends spring AbstractPreAuthenticatedProcessingFilter
2- Override "getPreAuthenticatedPrincipal". This method extracted the cookie value and return it. (Whatever the Object you return here is what you get as a parameter in UserDetailsService.loadUserByUsername).
3- Modified my service to implement spring UserDetailsServiceand inside loadUserByUsername did all authentication logic here and set all logged in user in CustomUser object. Then return it.
4- Whenever request matches /api/XYZ/**, Spring will call your CustomUser.getAuthorities and try to find ADMIN role there.

Categories