EDIT: I am now getting a 403 forbidden, access denied on all requests besides register and login
I am testing out my endpoints for an application after having implemented JWT authentication and spring security based on this article: https://medium.com/#hantsy/protect-rest-apis-with-spring-security-and-jwt-5fbc90305cc5
Similarly, my other sources of inspiration include: https://www.youtube.com/watch?v=X80nJ5T7YpE
Registration and login of a user are functional, but when I perform subsequent requests requiring the JWT in the header, I get an infinite loop/recursion and a stack overflow error. However, when I input a non-existing JWT on purpose, it throws back the appropriate error message and works well. So the problem is with successfully authenticating a valid JWT. After looking online at different stack overflow answers such as:
Infinite loop in custom Spring Security application
Infinite loop in custom Spring Security application
In Spring Security 3.2.5, what is causing an infinite loop inside the AuthenticationManager implementation?
They all seem to say the same thing which is that the ProviderManager class is looking through the list of auth providers to find one that can authenticate the given authentication and it is not finding one , which causes this error, and they suggest to implement your own authentication provider. Below is my code for the different relevant classes. Note that I do not use a userDetailsService class; instead I have my own UserService class which has basically the functionality of userDetailsService traditionally and also application-specific code. I also did not implement my own authentication provider, following the code examples I mentioned above, so I would like to avoid implementing the authentication providers since the code samples mentioned work and are simpler.
JwtProvider class
#Component
public class JwtProvider {
#Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";
#Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h
#Autowired
private AppUserService appUserService;
// encoding the secret used to create the signature for the JWT
#PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
public String createToken(String userEmail) {
Claims claims = Jwts.claims().setSubject(userEmail); // subject is the person who is being authenticated
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()//
.setClaims(claims)//
.setIssuedAt(now)//
.setExpiration(validity)//
.signWith(SignatureAlgorithm.HS256, secretKey)//
.compact();
}
public Authentication getAuthentication(String token) {
AppUser user = this.appUserService.getAppUserByEmail(getUserEmail(token));
return new UsernamePasswordAuthenticationToken(user, "");
}
// get userEmail in the jwt token that we receive
public String getUserEmail(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
// validate a token that we have: not expired, and matches the userEmail
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
JwtConfigurer class
package ca.mcgill.ecse321.petadoption.controller;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
// configures how we filter http requests
// specifies order of applying filters
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private JwtProvider jwtProvider;
public JwtConfigurer(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
#Override
public void configure(HttpSecurity http) throws Exception {
JwtFilter customFilter = new JwtFilter(jwtProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtFilter class
// Job: intercept a request and look at the header to get the JWT
public class JwtFilter extends GenericFilterBean {
private JwtProvider jwtProvider;
public JwtFilter(JwtProvider jwtProvider) {
this.jwtProvider = jwtProvider;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
String token = resolveToken((HttpServletRequest) req);
if (token != null && jwtProvider.validateToken(token)) { // if token is not null and it isn't expired and it is valid
Authentication auth = jwtProvider.getAuthentication(token); // returning the UsernamePasswordAuthenticationToken
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(req, res);
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
SecurityConfigurer class
#Configuration
#EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
#Autowired
JwtProvider jwtProvider;
#Autowired
AppUserService appUserService;
// this is because we cannot perform #autowired authManager anymore cuz its only compatible w/ older Spring Boot
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable() // this disables cross-site request forgery where an attacker manages to get an authenticated user to perform a malicious request to his liking using diff links and such
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // tell it to use the filterChain that intercepts requests and checks the authorization header for a jwt
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/login/").permitAll()
.antMatchers("/register").permitAll()
.antMatchers("/register/").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtProvider)); // this specifies to add our custom filter before the usernamepasswordauthenticationfilter
}
}
Here is the error I get:
2020-03-14 12:14:27.183 ERROR 784 --- [nio-8081-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Filter execution threw an exception] with root cause
java.lang.StackOverflowError: null
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at com.sun.proxy.$Proxy120.authenticate(Unknown Source) ~[na:na]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:195) ~[spring-security-core-5.2.1.RELEASE.jar:5.2.1.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:501) ~[spring-security-config-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at sun.reflect.GeneratedMethodAccessor107.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
I am relatively new to Spring and Spring Boot so any help is appreciated thank you
Related
I have added Http cookie Authentication using authentication manager to my Spring Boot REST API
I have a controller that exposes a rest service allowing authentication to /api/auth/signin resource via Spring security cookies session.
Here is the the Controller and the security configuration This exemple.
After running the application, I noticed that it is important to carry out the unit test part, so I wanted to create mocks for the authenticateUser method (resource: /signin), but unfortunately I encountered problems.
Voici la classe AuthControllerTest:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes=Application.class)
#WebMvcTest(AuthController.class)
public class AuthControllerTest {
#MockBean
UserRepository userRepository;
#MockBean
AuthenticationManager authenticationManager;
#MockBean
private UserDetailsServiceImpl userDetailsServiceImpl;
#Autowired
private MockMvc mockMvc;
private static UserDetailsImpl dummy;
#MockBean
private JwtUtils jwtUtil;
#Autowired
WebApplicationContext webApplicationContext ;
private ResponseCookie cookies;
#BeforeEach
public void setUp() {
dummy = new UserDetailsImpl(10L,"test1","test1#mail.com","123456",new ArrayList<>());
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();*/
cookies = jwtUtil.generateJwtCookie(dummy) ;
}
#Test
#DisplayName("POST /signin")
void authenticateUser() throws Exception
{
LoginRequest authenticationRequest = new LoginRequest("mod", "123456") ;
String jsonRequest = asJsonString(authenticationRequest);
RequestBuilder request = MockMvcRequestBuilders
.post("/api/auth/signin")
.content(jsonRequest)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON);
Authentication auth = Mockito.mock(Authentication.class);
Mockito.when(auth.getName()).thenReturn("authName");
auth.setAuthenticated(true);
Mockito.when(auth.isAuthenticated()).thenReturn(true);
Mockito.when(authenticationManager.authenticate(auth)).thenReturn(auth); // Failing here
Mockito.when(jwtUtil.generateJwtCookie(dummy)).thenReturn(cookies);
Mockito.when(userDetailsServiceImpl.loadUserByUsername("test1")).thenReturn(dummy);
MvcResult mvcResult = mockMvc.perform(request)
.andExpect(status().is2xxSuccessful())
.andReturn();
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Here is the encountered errors after running the class AuthControllerTest:
java.lang.AssertionError: Range for response status value 403
expected: but was:<CLIENT_ERROR> Expected :SUCCESSFUL
Actual :CLIENT_ERROR
at
org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
at
org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
at
org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$is2xxSuccessful$3(StatusResultMatchers.java:78)
at
org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:212)
at AuthControllerTest.authenticateUser(AuthControllerTest.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498) at
org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at
org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at
org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at
org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at
org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at
org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at
org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at
org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at
org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
If you willing to change your code, then do this and hopefully everything will work fine:
A. Create a package in your test main package, it should include both words test and integration
package com.<yourApplication>.test.integration;
B.This is how your test class should be like:
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Import({ ObjectMapper.class, <YourController>.class })
#TestMethodOrder(OrderAnnotation.class)
class YourTestClass {
#Autowired
private MockMvc mockMvc;
#Autowired
private ObjectMapper objectMapper;
// user authentication
private static String jwt; // can use this for your next test request
#Test
#Order(1)
#DisplayName("User Authentication token")
void authenticationTest() throws JsonProcessingException, Exception {
final String link = "/api/auth/signin";
AuthenticationRequest defaultAuth = new AuthenticationRequest("admin", "admin");
System.out.println(objectMapper.writeValueAsString(defaultAuth));
// perform the request
MvcResult result = this.mockMvc
.perform(MockMvcRequestBuilders.post(link)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(defaultAuth)))
.andExpect(MockMvcResultMatchers.status().isOk())
.andReturn();
String response = result.getResponse().getContentAsString();
System.out.println("from response: " + response); //
JsonNode root = objectMapper.readTree(response);
JsonNode jwtvalue = root.get("jwt");
jwt = jwtvalue.textValue();
System.out.println("jwt deserlized: " + jwt);
}
}
C. If the request returned an error, then the problem is either in your controller or the way you setup the JWT authentication.
I have an error when I try to create a post request in my Mock test. It tells me that the authenticationManager is null(meaning it wasn't autowired).
I have a Login Controller having an autowired AuthenticationManager with three methods(login/register/userinfo) .
I tried to create a Login Controller Test with an instance of LoginController having injectedMocks but I don't know how to use it(I am not accustomed to testing).
Login Controller
#RestController
#RequestMapping("/auth")
#CrossOrigin
public class LoginController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
JWTTokenHelper jWTTokenHelper;
#Qualifier("myUserDetails")
#Autowired
private UserDetailsService userDetailsService;
String jwtToken;
private final UserService userService;
#Autowired
LoginController(UserService userService){
this.userService = userService;
}
#PostMapping(value = "/register",consumes = "application/json")
public ResponseEntity<?> register(#RequestBody RegistrationDTO registrationDTO) throws InvalidKeySpecException, NoSuchAlgorithmException {
return this.login(new AuthenticationDTO(registrationDTO.getEmail(), registrationDTO.getPassword()));
}
#PostMapping(value = "/login",consumes = "application/json")
public ResponseEntity<?> login(#RequestBody AuthenticationDTO authenticationDTO) throws InvalidKeySpecException, NoSuchAlgorithmException {
System.out.print("\n" + authenticationDTO.getEmail() + " " + authenticationDTO.getPassword() + "\n");
final Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
authenticationDTO.getEmail(), authenticationDTO.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetails userDetails=(UserDetails) authentication.getPrincipal();
String jwtToken=jWTTokenHelper.generateToken(userDetails.getUsername());
this.jwtToken = jwtToken;
LoginResponseDTO responseDTO = new LoginResponseDTO(jwtToken);
//try to find out if it's a customer or not
return ResponseEntity.ok(responseDTO);
}
#GetMapping("/userinfo")
#ResponseBody
public ResponseEntity<?> getUserInfo(Principal user){
User userObj = (User)userDetailsService.loadUserByUsername(user.getName());
UserDTO userInfo = new UserDTO(userObj.getId(),userObj.getName(),userObj.getEmail(),userObj.getAuthorities().toArray());
return ResponseEntity.ok(userInfo);
}
}
Login Controller Test
import com.example.demo.Controller.LoginController;
import com.example.demo.Model.DTOs.AuthenticationDTO;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
#AutoConfigureMockMvc
#RunWith(MockitoJUnitRunner.class)
public class LoginControllerTest {
#Autowired
private MockMvc mockMvc;
#InjectMocks
private LoginController loginController;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.standaloneSetup(loginController).build();
}
#Test
public void testLogin() throws Exception{
AuthenticationDTO authenticationDTO = new AuthenticationDTO("ztudorita#gmail.com","Shaorma72.");
mockMvc.perform(
MockMvcRequestBuilders
.post("/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(authenticationDTO))
// .accept(MediaType.APPLICATION_JSON)
)
.andReturn();
}
public static String asJsonString(final Object obj) {
try {
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ERROR CONSOLE
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:199)
at com.example.demo.ControllerTest.LoginControllerTest.testLogin(LoginControllerTest.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:55)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:100)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:107)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:41)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: java.lang.NullPointerException
at com.example.demo.Controller.LoginController.login(LoginController.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
I am using RestTemplate with Ribbon Client #LoadBalanced. When I am calling my service - time-service(logical identifier) using Discovery Server, which is running on two instances, it is throwing null pointer exception.
My two instances of time-service are running properly. Also, in discovery server both are registered along with this Ribbon client.
Below is the code for same -
#SpringBootApplication
#RestController
#EnableDiscoveryClient
public class RibbonTimeAppApplication {
#Inject
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(RibbonTimeAppApplication.class, args);
}
#Bean
#LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
#GetMapping
public String getTime()
{
String response = "";
try {
response = restTemplate.getForEntity("http://time-service", String.class).getBody();
}
catch(Exception e)
{
e.printStackTrace();
}
return response;
}
}
Here is the stacktrace I am getting when calling this service -
java.lang.NullPointerException
at com.javatechstack.RibbonTimeAppApplication.getTime(RibbonTimeAppApplication.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at .......
.........................
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
This is the screenshot of Eureka dashboard -
You can avoid this error by making restTemplate static. Please confirm if it works:
#Bean
#LoadBalanced
public static RestTemplate restTemplate() {
return new RestTemplate();
}
I am using Spring Batch in a Spring Boot application as below. Spring Batch and the application seem to work fine with that configuration.
However, with that configuration for a simple Dao-Test (not for Spring Batch) I get the following exception. Without the Spring Batch configuration the test is running fine. Update: The problem appears if I configure to use an own JobRepository with a transaction manager (below in class MyBatchConfigurer).
I tried to provide another transaction manager for Spring Batch but I am running from exception to exception.
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
Caused by: javax.persistence.TransactionRequiredException: no transaction is in progress
at org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3505)
at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1427)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1423)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:350)
at com.sun.proxy.$Proxy165.flush(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:305)
at com.sun.proxy.$Proxy165.flush(Unknown Source)
at com.foo.dao.GenericDao.save(GenericDao.java:60)
at com.foo.dao.GenericDao$$FastClassBySpringCGLIB$$71a0996b.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
... 37 more
Test Setup
#SpringBootTest
#RunWith(SpringRunner.class)
#EntityScan(basePackages = "com.foo.entity")
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
#TestPropertySource("/mytest.properties")
#Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = "classpath:/testdata.sql")
public class MyTest {
#Inject
private OrderDao sut;
#Test
public void test_findByAnotherFieldId() {
final Order order = // build an order ...
sut.save(order);
final Order result = sut.findByAnotherFieldId("valueOfOtherField");
assertThat(result).isEqualTo(order);
}
}
Spring Batch Job configuration
#Configuration
#EnableBatchProcessing
#Import(OracleDatabaseConfig.class)
#ComponentScan(basePackageClasses = MyBatchConfigurer.class)
public class BatchJobConfig {
// Injects...
#Qualifier("analyseOrdersJob")
#Bean
public Job analyseOrdersJob() {
return jobBuilderFactory.get("analyseOrdersJob").start(analyseOrdersStep()).build();
}
#Bean
public Step analyseOrdersStep() {
return stepBuilderFactory.get("analyseOrdersStep").<Order, String>chunk(4)
.reader(orderItemReader) //
.processor(orderItemProcessor) //
.writer(orderItemWriter) //
.build();
}
}
Spring Batch configuration
#Component
public class MyBatchConfigurer extends DefaultBatchConfigurer {
#Inject
private DataSource dataSource;
#Override
public JobRepository getJobRepository() {
try {
return extractJobRepository();
} catch (final Exception e) {
throw new BatchConfigurationException(e);
}
}
private JobRepository extractJobRepository() throws Exception {
final JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDataSource(dataSource);
factory.setTransactionManager(getTransactionManager());
factory.afterPropertiesSet();
return factory.getObject();
}
}
ItemReader
#Component
#StepScope
public class OrderItemReader implements ItemReader<Order> {
#Inject
private OrderDao orderdao;
private int nextOrderIndex;
private List<Order> orders;
#PostConstruct
public void postConstruct() {
orders = orderdao.findAll();
}
#Override
public Order read() {
if (nextOrderIndex < orders.size()) {
final Order order = orders.get(nextOrderIndex);
nextOrderIndex++;
return order;
}
return null;
}
}
ItemWriter and ItemProcessor are similarly configured.
I have problem trying to add authentication on websockets using spring security.
At this point I have configured http to work with the current security configuration and it works fine - I am able to authenticate users and it returns authenticated principal. However I want to add some websocket security and as soon as I put the following class the app logs me but it does not want to open any websockets:
#Configuration
public class WebSocketSecurityConfig extends
AbstractSecurityWebSocketMessageBrokerConfigurer {
protected void configureInbound(
MessageSecurityMetadataSourceRegistry messages) {
messages.nullDestMatcher().permitAll().simpDestMatchers("/app/**")
.authenticated()
.simpSubscribeDestMatchers("/user/*", "/topic/*")
.authenticated();
// TODO
}
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
The WebSocketSecurityConfig is correctly initialized because when I add permitAll() instead of authenticated() it allows me to open websocket.
I am sure that if the http principal is authenticated the websocket principal is also authenticated. I used this as a reference:
http://docs.spring.io/spring-security/site/docs/current/reference/html/websocket.html
Here is the error in the console:
org.springframework.messaging.MessageDeliveryException: Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:127)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:104)
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:266)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:309)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:72)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:112)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:79)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:393)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:494)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:289)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:56)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:203)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:203)
at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:92)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:609)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
at org.springframework.security.messaging.access.intercept.ChannelSecurityInterceptor.preSend(ChannelSecurityInterceptor.java:71)
at org.springframework.messaging.support.AbstractMessageChannel$ChannelInterceptorChain.applyPreSend(AbstractMessageChannel.java:158)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:113)
... 24 more
My configuration class:
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Autowired
HandshakeHandler handshakeManager;
#Autowired
AbstractManagerEventListener eventListenerHandler;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void configureWebSocketTransport(
WebSocketTransportRegistration registration) {
registration.setMessageSizeLimit(512 * 1024);
registration.setSendBufferSizeLimit(1024 * 1024);
registration.setSendTimeLimit(40000);
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/channelSocket").setHandshakeHandler(
handshakeManager);
}
#Bean
public HandshakeHandler handshakeManager() throws Exception {
return new HandshakeManager();
}
}
And my HandshakeHandler class:
public class HandshakeManager extends DefaultHandshakeHandler {
#Override
protected Principal determineUser(ServerHttpRequest request,
WebSocketHandler wsHandler, Map<String, Object> attributes) {
Principal principal = request.getPrincipal();
return new UserPrincipal(principal.getName());
}
}
The determineUser(ServerHttpRequest request,
WebSocketHandler wsHandler, Map<String, Object> attributes)
returns the same principal as the principal of the user who is authenticated.
Any help is appreciated!Thank you!