Reputation: 161
I'm working on a Java Springboot REST API. In order to access the endpoints, users must send a request to an external Identity Server service, which will return a token. That token will then be sent in the header Authorization to this API, which will check if the user is in the database before allowing the request to go through to the controller.
I'm a bit new to Java so I used some examples on the internet on how to make this happen. I reached a point where the request comes in, gets filtered, and then I can allow it to go though or not. Now I need to add the part where I check the database to see if the user is there. I'm having some problems with this.
I added the following packages to gradle:
Here is the code I have implemented inside the security package. This is before trying to add the database integration, so it runs and works:
WebSecurityConfig.java:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests()
.antMatchers(httpMethod.GET, "/user").authenticated()
.and()
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
// this following one might not be necessary, it was in the example but I don't think it's being used
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("password")
.roles("ADMIN");
}
}
JWTAuthenticationFilter.java:
public class JWTAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
Authentication authentication = TokenAuthenticationService.getAuthentication((HttpServletRequest) request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
TokenAuthenticationService.java:
public class TokenAuthenticationService {
static final String SECRET = "mySecret";
static final String TOKEN_PREFIX = "Bearer";
static final String HEADER_STRING = "Authorization";
static Authentication getAuthentication(httpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
// do a bunch of stuff with the token to get the user Identity
if (userID != null) {
// here I need to call a method from a class in gateway, to find this user in the database..
// if not found I'll return null
return new UsernamePasswordAuthenticationToken(userID, null, Collections.emptyList());
}
return null;
}
}
So that's the code that's working right now. However, I can't call external method from inside getAuthentication
because it's static, so in order to call the gateway method, I made it not-static.
Because I made it not-static, I had to change the way I called it in JWTAuthenticationFilter
. Instead of calling the method directly I had to add the line:
TokenAuthenticationService tokenAuthenticationService = new TokenAuthenticationService();
and then call getAuthentication
using tokenAuthenticationService
.
After that I tried to call the method userGateway.getByUserID
directly. But I need an instant of UserGateway
for that. I can't initialize an instance of UserGatewayImplementation
directly.
Not only is it against the principles of dependency injection that we follow in this project, it would also require initializing something else that is used by that class. That something else also requires another object and so on.
So I added the annotation @RequiredArgsConstructor
to the class, and gave it the following:
private final UserGateway userGateway;
so that I could call this.userGateway.getByUserID(userID)
.
However, because I had to create an instance of TokenAuthenticationService
(because the method isn't static anymore), and I added an attribute to TokenAuthenticationService
(userGateway
), it wants me to pass an instance of UserGateway
to the constructor when I create tokenAuthenticationService
in JWTAuthenticationFilter
.
Just like before, I can't do that. So I added @RequiredArgsConstructor
to the class JWTAuthenticationFilter
, and gave it this attribute:
private final TokenAuthenticationService tokenAuthenticationService;
so that I could use it to call getAuthentication
.
This of course led to the same problem in WebSecurityConfig
. In this line:
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
It creates an instance of JWTAuthenticationFilter
, but now it wants me to pass an instance of TokenAuthenticationService
to it, because it has that attribute.
So I did the same thing, added @RequiredArgsConstructor
to the WebSecurityConfig
class, and gave it this attribute:
private final JWTAuthenticationFilter jwtAuthenticationFilter;
and then passed this jwtAuthenticationFilter
to addFilterBefore
.
Doing all of this made the editor stop complaining, but when I try to run the application, it gives me the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in (path).security.WebSecurityConfig required a bean of type '(path).security.JWTAuthenticationFilter' that could not be found.
Action:
Consider defining a bean of type '(path).security.JWTAuthenticationFilter' in your configuration.
I googled this error and tried to add @Bean
to JWTAuthenticationFilter
, to doFilter
, etc, but it didn't work and I'm not surprised, because I was doing it blindly.
I'd appreciate any help with this, even if it's brief. At the end of the day, I just want to be able to call a method from another class in getAuthentication
, to check the database and see if the user is there.
I obviously need to learn more about Java and Springboot, but unfortunately I'm in a hurry to make this work.
Upvotes: 0
Views: 581
Reputation: 161
After trying a bunch of things blindly, I ended up finding the answer by myself. I just had to add the annotation @Component
to the classes JWTAuthenticationFilter
and TokenAuthenticationService
. Can't quite explain it at this point, but I'll leave it here in case anyone else ever needs it.
Upvotes: 1