I wanna run a schedule method and in this method I need to use information about user is logged in. However, when I run getPrincipal() my code gets nullPointException
#Component
public class Import extends WebSecurityConfigurerAdapter {
#Autowired
private ActivityRepository activityRepository;
#Scheduled(fixedRate = 300000)
public void importActivities() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Principal principal = (Principal) authentication.getPrincipal();
List<Activity> lastActivity = activityRepository.findFirstByOrderByStartDateDesc();
int lastEpoch = 0;
if (lastActivity.isEmpty() == false) {
lastEpoch = (int) (lastActivity.get(0).getStartDate().getTime() / 1000);
}
System.out.println(lastEpoch);
final RestTemplate restTemplate = new RestTemplate();
final HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(getAccessToken(principal));
final HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<List<Activity>> rateResponse = restTemplate.exchange(
"https://www.strava.com/api/v3/athlete/activities?after=" + lastEpoch, HttpMethod.GET, entity,
new ParameterizedTypeReference<List<Activity>>() {
});
List<Activity> activities = rateResponse.getBody();
activityRepository.saveAll(activities);
}
private String getAccessToken(final Principal principal) {
final OAuth2Authentication oauth2Auth = (OAuth2Authentication) principal;
final OAuth2AuthenticationDetails oauth2AuthDetails = (OAuth2AuthenticationDetails) oauth2Auth.getDetails();
return oauth2AuthDetails.getTokenValue();
}
}
Thanks
Related
I have configured swagger to use login / password as followed:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket SwaggerApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("cms")
.select().apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext())).pathMapping("/")
.useDefaultResponseMessages(false)
.apiInfo(apiInfo());
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.ant("/**"))
.build();
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[3];
authorizationScopes[0] = new AuthorizationScope("read", "read all");
authorizationScopes[1] = new AuthorizationScope("trust", "trust all");
authorizationScopes[2] = new AuthorizationScope("write", "write all");
return Collections.singletonList(new SecurityReference("oauth2schema", authorizationScopes));
}
#Bean
public SecurityConfiguration securityInfo() {
return new SecurityConfiguration("app", "app-secret", "", "", "", ApiKeyVehicle.HEADER, "", " ");
}
private OAuth securitySchema() {
List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
authorizationScopeList.add(new AuthorizationScope("read", "read all"));
authorizationScopeList.add(new AuthorizationScope("trust", "trust all"));
authorizationScopeList.add(new AuthorizationScope("write", "access all"));
List<GrantType> grantTypes = new ArrayList<>();
GrantType creGrant = new ResourceOwnerPasswordCredentialsGrant("http://localhost/swaggerAuth");
grantTypes.add(creGrant);
return new OAuth("oauth2schema", authorizationScopeList, grantTypes);
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Application")
.version("1.0")
.build();
}
And this is my method for autorization:
#RequestMapping(value = "/swaggerAuth", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = {MediaType.APPLICATION_ATOM_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<?> authenticate(#RequestBody MultiValueMap<String, String> formData) {
String username = formData.get("username").get(0);
String password = formData.get("password").get(0);
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
final UserDetails userDetails = userDetailsService.loadUserByUsername(username );
return jwtTokenUtil.generateToken(userDetails);
}
When I login via Swagger all is fine. User gets authneticated and Authentication is set to SecurityContextHolder.
But with every next swagger request the user is anonymousUser and not the one I authenticated with.
What is wrong with my configuration?
EDIT:
The token I return from authorization controller is not beeing sent in swagger request headers...
The problem was that in authenticate() method I was returning plain string. Instead I should return an object with access_token string field:
public class SwaggerAuthenticationResponse {
private final String access_token;
public SwaggerAuthenticationResponse(String access_token) {
this.access_token = access_token;
}
public String getAccess_token() {
return this.access_token;
}
}
The standard JSON format that an authorization server usually gives you, has a property named "expires_in", but now I'm working with an autorization server that gives me a property named "access_token_expires_in". Because of this, my OAuth2AccessToken always returns isExpired to false even when then access_token is expired, and that makes sens because it's trying to read the "expires_in" property that dose not exist. The getAdditionalInformation from OAuth2AccessToken returns my "access_token_expires_in" property value with 18000.
I was wondering if I can tell spring to use the "access_token_expires_in" property as an expiration value for my access_token?
My code:
#Configuration
class OAuth2RestConfiguration {
#Bean
protected OAuth2ProtectedResourceDetails resource() {
final ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setAccessTokenUri("<tokenUri>");
resourceDetails.setClientId("<clientId>");
resourceDetails.setClientSecret("<clientSecret>");
return resourceDetails;
}
#Bean
public OAuth2RestTemplate restTemplate() throws Exception {
final AccessTokenRequest atr = new DefaultAccessTokenRequest();
final OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource(),
new DefaultOAuth2ClientContext(atr));
return oAuth2RestTemplate;
}
}
Authorization server response sample:
{
"refresh_token_expires_in": 0,
"access_token": "<access_token>",
"access_token_expires_in": 18000,
"token_type": "bearer"
}
EDIT 1:
As a workaround, I've extended the OAuth2RestTemplate class and override the getAccessToken method:
public class CustomOAuth2RestTemplate extends OAuth2RestTemplate {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomOAuth2RestTemplate.class);
private OAuth2ClientContext context;
private Long LAST_RESET = getCurrentTimeSeconds();
private Long FORCE_EXPIRATION;
public CustomOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource) {
super(resource);
this.context = super.getOAuth2ClientContext();
this.FORCE_EXPIRATION = 10800L;
}
public CustomOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource,
DefaultOAuth2ClientContext defaultOAuth2ClientContext, Long forceExpiration) {
super(resource, defaultOAuth2ClientContext);
this.context = defaultOAuth2ClientContext;
this.FORCE_EXPIRATION = Objects.requireNonNull(forceExpiration, "Please set expiration!");
}
#Override
public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException {
OAuth2AccessToken accessToken = context.getAccessToken();
final Long diff = getCurrentTimeSeconds() - LAST_RESET;
/*
Either use a hardcoded variable or use the value stored in
context.getAccessToken().getAdditionalInformation().
*/
if (diff > FORCE_EXPIRATION) {
LOGGER.info("Access token has expired! Generating new one...");
this.LAST_RESET = getCurrentTimeSeconds();
context.setAccessToken(null);
accessToken = acquireAccessToken(context);
} else {
accessToken = super.getAccessToken();
}
LOGGER.info("Access token: " + context.getAccessToken().getValue());
return accessToken;
}
private Long getCurrentTimeSeconds() {
return System.currentTimeMillis() / 1000L;
}
}
And now the bean:
#Bean
public OAuth2RestTemplate restTemplate() throws Exception {
final AccessTokenRequest atr = new DefaultAccessTokenRequest();
final OAuth2RestTemplate oAuth2RestTemplate = new CustomOAuth2RestTemplate(resource(),
new DefaultOAuth2ClientContext(atr), 10800L); //example: 3h
oAuth2RestTemplate.setRequestFactory(customRequestFactory());
return oAuth2RestTemplate;
}
EDIT 2:
After I analyzed the OAuth2RestTemplate class more thoroughly, code refactoring was required:
public class CustomOAuth2RestTemplate extends OAuth2RestTemplate {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomOAuth2RestTemplate.class);
private Long LAST_RESET = getCurrentTimeSeconds();
private Long FORCE_EXPIRATION;
public CustomOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource) {
super(resource);
this.FORCE_EXPIRATION = 10800L; //3h
}
public CustomOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource,
DefaultOAuth2ClientContext defaultOAuth2ClientContext, Long forceExpiration) {
super(resource, defaultOAuth2ClientContext);
this.FORCE_EXPIRATION = Objects.requireNonNull(forceExpiration, "Please set expiration!");
}
#Override
public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException {
final Long diff = getCurrentTimeSeconds() - LAST_RESET;
/*
Either use a hardcoded variable or use the value stored in
context.getAccessToken().getAdditionalInformation().
*/
if (diff > FORCE_EXPIRATION) {
LOGGER.info("Access token has expired! Generating new one...");
this.LAST_RESET = getCurrentTimeSeconds();
final OAuth2ClientContext oAuth2ClientContext = getOAuth2ClientContext();
oAuth2ClientContext.setAccessToken(null);
return acquireAccessToken(oAuth2ClientContext);
}
return super.getAccessToken();
}
private Long getCurrentTimeSeconds() {
return System.currentTimeMillis() / 1000L;
}
}
You can add custom parameter by implementing TokenEnhancer interface and overriding its method as follows:
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
final Map<String, Object> additionalInfo = new HashMap<>();
// additionalInfo.put("CUSTOM_PARAM1", "CUSTOM_VALUE1");
additionalInfo.put("username", authentication.getPrincipal());//adding username param
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter {
#Override
public void configure(final AuthorizationServerEndpointsConfigurer
endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.tokenStore(tokenStore)
.tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
Hope it helps!
I'll post this as an answer since it works just fine. The only thing that was added was synchronized for concurrency, so that multiple access tokens should never be requested.
Final code:
public class CustomOAuth2RestTemplate extends OAuth2RestTemplate {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomOAuth2RestTemplate.class);
private Long LAST_RESET = getCurrentTimeSeconds();
private Long FORCE_EXPIRATION;
public CustomOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource) {
super(resource);
this.FORCE_EXPIRATION = 10800L; // 3h
}
public CustomOAuth2RestTemplate(OAuth2ProtectedResourceDetails resource,
DefaultOAuth2ClientContext defaultOAuth2ClientContext, Long forceExpiration) {
super(resource, defaultOAuth2ClientContext);
this.FORCE_EXPIRATION = Objects.requireNonNull(forceExpiration, "Please set expiration!");
}
#Override
public synchronized OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException {
final Long diff = getCurrentTimeSeconds() - LAST_RESET;
/*
* Either use a hardcoded variable or use the value stored in
* context.getAccessToken().getAdditionalInformation().
*/
if (diff > FORCE_EXPIRATION) {
LOGGER.info("Access token has expired! Generating new one...");
this.LAST_RESET = getCurrentTimeSeconds();
final OAuth2ClientContext oAuth2ClientContext = getOAuth2ClientContext();
oAuth2ClientContext.setAccessToken(null);
return acquireAccessToken(oAuth2ClientContext);
}
return super.getAccessToken();
}
private Long getCurrentTimeSeconds() {
return System.currentTimeMillis() / 1000L;
}
}
I am writing Unit Tests for the below REST Controller which takes a UserID and grants a List of Authorities to that user.
#RestController
#RequestMapping("/user")
#Api(value = "User", description = "User API")
public class UserController{
// some code
#RequestMapping(method = RequestMethod.POST, value = "/{userId}/grantAuthz")
#ApiOperation(value = "GrantAuthz", notes = "Grant Authorization")
public Collection<UserEntity.UserAuthz> grantAuthz(#PathVariable("userId") String userId,
#RequestBody ArrayList<String> authorities) {
UserEntity userEntity = userRepository.findOne(userId);
if(userEntity == null) {
//TODO: throw and send resource not found
return null;
}
log.debug("Authorities to be granted to user " + userId + " are : " + authorities);
for(String authz : authorities) {
log.debug("Adding Authorization " + authz);
userEntity.addUserAuthz(authz);
}
userRepository.save(userEntity);
return userEntity.getAuthorities();
}
}
I wrote the below Unit Test for the UserController
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class UserControllerTest {
private final Log log = LogFactory.getLog(getClass());
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
private MockMvc mockMvc;
private HttpMessageConverter mappingJackson2HttpMessageConverter;
private final String USER_URL = "/{userId}/grantAuthz";
private final String USER_ID = "111";
private final String USER_NAME = "MockUser";
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private UserRepository userRepository;
private String createdToken = null;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream().filter(
hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get();
Assert.assertNotNull("the JSON message converter must not be null",
this.mappingJackson2HttpMessageConverter);
}
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testGrantAuthorizationForUser() throws Exception{
Optional<UserEntity> userEntityAuthz = userRepository.findOneByUsername(USER_NAME);
Set<String> expectedAuthzList = (LinkedHashSet)userEntityAuthz.get().getAuthorizations();
List<String> grantList = new ArrayList<>();
grantList.add("ABC");
grantList.add("DEF");
grantList.add("GHI");
grantList.add("JKL");
grantList.add("MNO");
grantList.add("PQR");
grantList.add("STU");
grantList.add("VWX");
grantList.add("YZA");
JSONObject json = new JSONObject();
json.put("grantList",grantList);
MvcResult grantAuthzResult = mockMvc.perform(MockMvcRequestBuilders.post(USER_URL)
.contentType(contentType)
.param("userId",USER_ID)
.param("authorities",json.toString()))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
}
}
When executed, my test is throwing an Illegal Argument Exception:
"Not enough variable values available to expand 'userId'"
I am sending the required URL Parameters using the .param() method in the test, what am I doing wrong ? I reffered this possible duplicate question but did not find it much useful. Using RestTemplate in Spring. Exception- Not enough variables available to expand
I found out what I am doing wrong, using param() method is not the right way here as I have #PathVariable and #RequestBody in my Controller Methods as the parameters.
public Collection<UserEntity.UserAuthz> grantAuthz(#PathVariable("userId") String userId,
#RequestBody ArrayList<String> authorities) {
So I passed the #PathVariable in the post() method of the test.
MockMvcRequestBuilders.post(USER_URL,USER_ID)
As the required type is #RequestBody ArrayList<String> instead of using the JSONObject I used JSONArrayand used the content() method to send the JSONArray as the string.
Here are the changes I have made to the Test Method.
#Test
public void testGrantAuthorizationForUser() throws Exception{
Optional<UserEntity> userEntityAuthz = userRepository.findOneByUsername(USER_NAME);
Set<String> expectedAuthzList = (LinkedHashSet)userEntityAuthz.get().getAuthorizations();
List<String> grantList = new ArrayList<>();
grantList.add("ABC");
grantList.add("DEF");
grantList.add("GHI");
grantList.add("JKL");
grantList.add("MNO");
grantList.add("PQR");
grantList.add("STU");
grantList.add("VWX");
grantList.add("YZA");
JSONArray json = new JSONArray();
MvcResult grantAuthzResult = mockMvc.perform(MockMvcRequestBuilders.post(USER_URL,USER_ID)
.contentType(contentType)
.content(json.toString()))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
}
#Test
public void getOneContactAPI() throws Exception {
String id = "8";
mvc.perform(MockMvcRequestBuilders.get("/api/contact/{id}",id).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("id").exists());
}
I have very strange problem. In simple project I used Spring-Boot with oAuth2 (it is exactly jhipster generated project).
In services I connect with remote controllers (remote API) by restTemplate class. And I created special class to store cookieSession access to this remote API (this class has Session scope).
During authorization I save cookieSession from remote API to Session Scope class, and then when I make request to other part of remote API I use this seesionCookie.
Problem is, when I make asynchronous requesting from AngulrJS then sometimes Session scope class exist and sometimes it doesn't have data (is empty), but when I refresh website I have this data (without making next authorization). Whan I make synchronous requests there is no problem.
#Service
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class AuthorizationOsipDataService implements Serializable {
private String cookieSession;
public String getCookieSession() {
return cookieSession;
}
public void setCookieSession(String cookieSession) {
this.cookieSession = cookieSession;
}
}
Service:
#Service
public class OsipService {
#Autowired
private RestTemplate restTemplate;
#Autowired
private AuthorizationOsipDataService authorizationOsipDataService;
public String signInToOsipAndGetCookieSession (String login, String password) throws SignInToOsipException {
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("j_username", login);
map.add("j_password", password);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(map, new HttpHeaders());
log.debug("Logging... user: '{}'", login);
ResponseEntity response = restTemplate.exchange(osipUrl + authorizationUrl, HttpMethod.POST, requestEntity, String.class);
if(isLogged(response)){
String cookieSession = response.getHeaders().getFirst(HttpHeaders.SET_COOKIE);
log.debug("Succes login, setting authorizationOsipDataService");
authorizationOsipDataService.setPassword(password);
authorizationOsipDataService.setUsername(login);
authorizationOsipDataService.setCookieSession(cookieSession);
selectCompanyContext("538880bde511f776304687e6");
if(hasRoleOsipLite().getBody()){
return cookieSession;
} else {
throw new SignInToOsipException("User doesn't has ROLE_OSIPLITE");
}
} else{
throw new SignInToOsipException("Login error, HttpSatus:"+ response.getStatusCode().toString());
}
}
private boolean isLogged(ResponseEntity response){
//if location contains '/signin', it means that there is redirect and signin is failed
return !response.getHeaders().getFirst(HttpHeaders.LOCATION).contains("osip/signin");
}
public ResponseEntity selectCompanyContext(String companyContextId){
HttpHeaders httpHeaders = makeHeadersWithJson();
HttpEntity<String> requestEntity = new HttpEntity<String>(httpHeaders);
log.debug("Selecting context... '{}' ", companyContextId);
return restTemplate.exchange(osipUrl + selectCompanyContextUrl + companyContextId, HttpMethod.GET, requestEntity, String.class);
}
public ResponseEntity<NipExistDTO> isExistNip(String nip){
HttpHeaders httpHeaders = makeHeadersWithJson();
HttpEntity<String> requestEntity = new HttpEntity<String>(httpHeaders);
log.debug("isExistTest for nip: '{}'", nip);
return restTemplate.exchange(osipUrl + existNipUrl + nip, HttpMethod.GET, requestEntity, NipExistDTO.class);
}
}
...
...
...
Controllers:
#RestController
#RequestMapping("/customer")
public class CustomerResource {
private final Logger log = LoggerFactory.getLogger(CustomerResource.class);
#Autowired
private OsipService osipService;
#RequestMapping(value = "nipExist", method = RequestMethod.GET)
public
#ResponseBody
ResponseEntity<NipExistDTO> isNipExist(#RequestParam String nip) throws SignInToOsipException {
return osipService.isExistNip(nip);
}
#RequestMapping(value = "add", method = RequestMethod.POST)
public
#ResponseBody
ResponseEntity addCustomer(#RequestBody NewCustomerDTO newCustomerDTO) throws SignInToOsipException {
return osipService.addCustomerToOsip(newCustomerDTO);
}
}
WebConfig (configuration of Session Scope)
public void onStartup(ServletContext servletContext) throws ServletException {
log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles()));
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
if (!env.acceptsProfiles(Constants.SPRING_PROFILE_FAST)) {
initMetrics(servletContext, disps);
}
if (env.acceptsProfiles(Constants.SPRING_PROFILE_PRODUCTION)) {
initCachingHttpHeadersFilter(servletContext, disps);
initStaticResourcesProductionFilter(servletContext, disps);
initGzipFilter(servletContext, disps);
}
log.info("Web application fully configured");
servletContext.addListener(new RequestContextListener());
}
AngularJS
angular.module('osipliteApp')
.controller('CustomerController', function ($rootScope, $scope, Upload, $timeout,Customer,Scenario,Dictionary,$loading,$state,Auth) {
$loading.start('addCustomer');
$scope.isCollapsed=true;
//**** Initializing fields ****//
$scope.customerDTO = {name: null, nip: null, street: null,streetNumber:null, postOffice:null, zipCode:null, phoneNumber1: null, surveyNotes:null};
$scope.personEditDTO = {name: null, email:null,code1:null, phone1:null};
$scope.newCustomerDTO = {customerType: null, scenarioId:null};
$scope.personEditDTO.code1= '+48';
$scope.customerTypes = [{name:"Osoba fizyczna",value:"NATURAL_PERSON"},{name:"Jednostka budżetowa",value:"BUDGETARY_UNITS"},{name:"Spółka prawa handlowego",value:"COMMERCIAL"},{name:"Osoba fizyczna prowadząca działalność gospodarczą",value:"NATURAL_PERSON_WITH_BUSINESS"}];
$scope.products = Dictionary.get({dictionaryCode: 'PRODUCT_TYPE',languageCode:"PL"},function(success){
$scope.scenariosList = Scenario.get({value:'active'},function(success){$loading.finish('addCustomer');},function(error){restErrorHandler(error);});
},function(error){restErrorHandler(error);});
$scope.clear = function () {
$scope.customerDTO = {name: null, nip: null, street: null,streetNumber:null, postOffice:null, zipCode:null, phoneNumber1: null, surveyNotes:null};
$scope.personEditDTO = {name: null, email:null,code1:"+48", phone1:null};
$scope.newCustomerDTO = {customerType: "NATURAL_PERSON", scenarioId:null};
$scope.nipInvalid = null;
$scope.nipExist = null;
clearSurvey();
};
...
...
I'm writing an messenger with JavaFX and Spring4 on client-site and Spring4 on server-site. I secured the server with spring-security 3.2. Now my Problem: I have a loginpage on the client witch sends the login information to spring-security and receive the JSESSIONID cookie. This works fine but when I try to send the JSESSIONID with my request I become an
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class org.messenger.rest.JSONConversationResult] and content type [text/html;charset=UTF-8]
Server Inizializer
public class SpringMvcInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {ApplicationConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
Server SecurityInizializer
public class SpringSecurityInitializer extends
AbstractSecurityWebApplicationInitializer {
}
Server SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DriverManagerDataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
String authQuery = "select userid, authority from user where userid = ?";
String userQuery = "select userid, pw, enabled from user where userid = ?";
auth.jdbcAuthentication().dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery(userQuery)
.authoritiesByUsernameQuery(authQuery);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/register").permitAll()
.antMatchers("/getconvs", "/getcontacts").hasRole("USER")
.and()
.formLogin()
.and()
.csrf().disable();
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return new de.daschner.messenger.security.AuthenticationEntryPoint();
}
#Bean
public SuccessHandler successHandler() {
return new SuccessHandler();
}
#Bean
public SimpleUrlAuthenticationFailureHandler failureHandler() {
return new SimpleUrlAuthenticationFailureHandler();
}
#Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(11);
}
}
Server requestmapping for the secured "page"
#RequestMapping(value="/getconvs", method={RequestMethod.GET},
produces={MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody JSONConversationResult getConvsList(HttpServletRequest request, #RequestParam(value="uid") String uid){
JSONConversationResult ret = new JSONConversationResult();
Map<String, Map<Date, String>> convs = convService.getConvsList(uid);
if (convs != null) {
ret.setConversations(convs);
ret.setMessage("OK");
ret.setError(0);
} else {
ret.setError(1);
ret.setMessage("Verbindungsfehler");
}
return ret;
}
Client send Login and get Cookie
Map<String, String> loginform = new HashMap<String, String>();
loginform.put("username", user);
loginform.put("password", pw);
HttpEntity<Map<String, String>> login = new HttpEntity<Map<String, String>>(loginform);
ResponseEntity<HttpServletResponse> response = restTemplate.exchange(
"http://localhost:8080/messenger-webapp/login",
HttpMethod.POST,
login,
HttpServletResponse.class);
HttpHeaders headers = response.getHeaders();
Set<String> keys = headers.keySet();
String cookie = "";
for (String header : keys) {
if (header.equals("Set-Cookie")) {
cookie = headers.get(header).get(0);
}
}
String jsessionid = cookie.split(";")[0];
conf.setJsessionid(jsessionid.split("=", 2)[1]);
return ret;
Client send JSESSIONID with request
ResponseEntity<JSONConversationResult> response = restTemplate.exchange(
"http://localhost:8080/messenger-webapp/getconvs?uid=" + uid,
HttpMethod.GET,
getAuthHeader(),
JSONConversationResult.class);
JSONConversationResult ret = response.getBody();
return ret;
private HttpEntity<String> getAuthHeader() {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", "JSESSIONID=" + config.getJsessionid());
return new HttpEntity<String>(requestHeaders);
}
I hope you can help me.
EDIT:
Ok I figured out that the problem was not that the JSESSIONID wasn't sent correctly. But my login was incorrect and my query to get the user from database.
The correct login-post
ClientHttpResponse response = restTemplate.execute(
"http://localhost:8080/messenger-webapp/login",
HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getBody().write(("username=" + user + "&password=" + pw).getBytes());
}
},
new ResponseExtractor<ClientHttpResponse>() {
#Override
public ClientHttpResponse extractData(ClientHttpResponse response)
throws IOException {
return response;
}
});
The correct query
String authQuery = "select u.userid, r.role_name from user u, role r, user_role a where u.dbid = a.user_id and r.dbid = a.role_id and u.userid = ?";
I hope this will help other people. If anyone has an alternative please let me know.
Ok I figured out that the problem was not that the JSESSIONID wasn't sent correctly. But my login was incorrect and my query to get the user from database.
The correct login-post
ClientHttpResponse response = restTemplate.execute(
"http://localhost:8080/messenger-webapp/login",
HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getBody().write(("username=" + user + "&password=" + pw).getBytes());
}
},
new ResponseExtractor<ClientHttpResponse>() {
#Override
public ClientHttpResponse extractData(ClientHttpResponse response)
throws IOException {
return response;
}
});
The correct query
String authQuery = "select u.userid, r.role_name from user u, role r, user_role a where u.dbid = a.user_id and r.dbid = a.role_id and u.userid = ?";
I hope this will help other people. If anyone has an alternative please let me know.