I am working on creating an app using springboot which would consume an API which has OAuth2 authentication. Post successful getting the Bearer code I would be calling another API which would actually give me data for further processing. I have custom OAuth url, authorization code, username, password, secret key, api key. When I searched on internet, none of the example were usign all of these[only secret key, authorization code and api key was getting used.]. Do I need to use username and password as well?
I tried below code [and few other things]. But not able to get through this.
<code>
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.DatatypeConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.support.BasicAuthorizationInterceptor;
import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#Component
public class ApiConsumer {
#Autowired
private RestTemplate template;
#Value("${oauth.api}")
String url;
#Value("${oauth.oAuth.url}")
String oAuthUrl;
#Value("${oauth.user}")
String username;
#Value("${oauth.password}")
String password;
#Value("${oauth.apikey}")
String apiKey;
#Value("${oauth.secretkey}")
String apiSecret;
public String postData() {
log.info("Call API");
try {
String response = consumeApi();
if (response.equals("200")) {
log.info("posting data to another api");
// CALL another API HERE for actual data with bearer code
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private String consumeApi() throws Exception {
String authorizationHeader = "Basic "
+ DatatypeConverter.printBase64Binary((apiKey + ":" + apiSecret).getBytes());
// setting up the HTTP Basic Authentication header value
HttpHeaders requestHeaders = new HttpHeaders();
// set up HTTP Basic Authentication Header
requestHeaders.add("Authorization", authorizationHeader);
requestHeaders.add("Accept", MediaType.APPLICATION_FORM_URLENCODED_VALUE);
requestHeaders.add("response_type", "code");
// request entity is created with request headers
HttpEntity<String> request = new HttpEntity<String>(requestHeaders);
template.getInterceptors().add(new BasicAuthorizationInterceptor(username, password));
ResponseEntity<String> result = null;
try {
result = template.exchange(oAuthUrl, HttpMethod.POST, request, String.class);
log.info( result.getBody());
if (result.getStatusCode() == HttpStatus.OK) {
transformData(result.getBody());
}
if (result.getStatusCode() != HttpStatus.REQUEST_TIMEOUT) {
throw new Exception("Api taking too long to respond! ");
}
}
catch (Exception e) {
log.error("Api taking too long to respond!");
}
return "";
}
private void transformData(String body) throws JsonMappingException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<HeapEntity> heapEntityList = Arrays.asList(mapper.readValue(body, HeapEntity[].class));
if (heapEntityList != null && heapEntityList.size() > 0) {
heapEntityList.forEach(i -> i.getPhoneNumber().replaceAll("-", ""));
}
log.debug("Size of list is :: " + heapEntityList.size());
heapEntityList.add(null);
}
}
</code>
Unfortunately, I cannot give a direct answer to your question, because it is not clear from it which grant type you are trying to use, and this will determine the answer to the question whether you need to use a username and password or not.
I advise you to familiarize yourself with the Section 4 of RFC 6749, in which you will find information on all grant types supported by the standard, and the request parameters they require.
Examples for the Password grant type:
If you need to use the RestTemplate, you can do something like this:
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/x-www-form-urlencoded");
headers.set("Authorization", "Basic " + Base64.getUrlEncoder().encodeToString((clientId + ":" + clientSecret).getBytes()));
String body = String.format("grant_type=password&username=%s&password=%s", username, password);
String json = restTemplate.postForObject(tokenUrl, new HttpEntity<>(body, headers), String.class);
Note that the response is a json object containing a token, not the token itself.
Or you can simply use the more appropriate for your purpose OAuth2RestTemplate:
#Bean
public OAuth2RestTemplate oAuth2RestTemplate() {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
resource.setAccessTokenUri("tokenUrl");
resource.setClientId("clientId");
resource.setClientSecret("clientSecret");
resource.setUsername("username");
resource.setPassword("password");
return new OAuth2RestTemplate(resource);
}
Do not forget to add #EnableOAuth2Client to one of your configuration classes.
Related
First of all, I don't want to provide a web view for social login on my mobile
The social login method for spring security is to call /oauth2/authorization/:registration_id and then /login/oauth2/code/:registration_id.
However, if you are using SDK on mobile, I don't need /oauth2/authorization/:registration_id, only need /login/oauth2/code/:registration_id.
However, it seems that only /login/oauth2/code/:registration_id is not provided.
Is there a way to use only /login/oauth2/code/:registration_id in spring security?
Your mobile app is an OAuth2 client. It should handle authentication with the authorization-server and then add Authorization header with Bearer token to the request it send to secured resource-server(s).
For authorization-server, two options:
use your "social" identity provider if it support OAuth2 but this is rarely possible, see below
put an authorization-server in front of "social" identity provider(s). This can be useful in many cases (Keycloak for instance works for all below and is in my opinion a way more mature solution than Spring's authorization-server):
you have several identity sources (Google, Facebook, etc)
you need to add role management to your users (some users need to be identified as moderators or whatever elevated privileges)
you have non OAuth2 identity sources (database or corporate LDAP for instance)
several identity sources but some of it are not issuing OpenID JWT access-tokens
Last, the resource-server (REST API) which can completely ignore login flows (which should be handled by client). All it needs is an Authorization header with a bearer token and either a JWT decoder or an introspection endpoint. See this article for configuring such a resource-server.
I configure my resource-servers to return 401 (unauthorized) when Authorization header is invalid or missing, and not 302 (redirect to login):
in my opinion, this is client responsibility to handle end-user login and also know which URIs require an Authorization (and from which issuer): if your app consumes resources from different providers (for instance Facebook plus Google Maps plus your own Spring services) it must know what access-token to associate with which request.
you can end with cases where your resource-server is exposed to several clients with different issuers. In that case, do you really want to put some code in your resource-servers to figure out to which authorization-server redirect depending on the client ID?
I don't know the side effect, but I found a way to work.
package com.example.demo.config.security;
import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.StreamSupport;
#Component
#RequiredArgsConstructor
public class CustomOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository<OAuth2AuthorizationRequest> {
private static final char PATH_DELIMITER = '/';
private final InMemoryClientRegistrationRepository clientRegistrationRepository;
#Override
public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {
ClientRegistration clientRegistration = resolveRegistration(request);
if (clientRegistration == null) {
return null;
}
String redirectUriAction = getAction(request, "login");
String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);
return OAuth2AuthorizationRequest.authorizationCode()
.attributes((attrs) -> attrs.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()))
.clientId(clientRegistration.getClientId())
.redirectUri(redirectUriStr)
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
.state(request.getParameter("state"))
.scopes(clientRegistration.getScopes())
.build();
}
#Override
public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) {
}
#Override
public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {
return loadAuthorizationRequest(request);
}
private ClientRegistration resolveRegistration(HttpServletRequest request) {
return StreamSupport.stream(clientRegistrationRepository.spliterator(), false)
.filter(registration -> {
return Optional
.ofNullable(UriComponentsBuilder
.fromHttpUrl(registration.getRedirectUri())
.build().getPath())
.orElse("")
.equals(request.getRequestURI());
})
.findFirst()
.orElse(null);
}
private String getAction(HttpServletRequest request, String defaultAction) {
String action = request.getParameter("action");
if (action == null) {
return defaultAction;
}
return action;
}
private static String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration,
String action) {
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
// #formatter:off
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
.build();
// #formatter:on
String scheme = uriComponents.getScheme();
uriVariables.put("baseScheme", (scheme != null) ? scheme : "");
String host = uriComponents.getHost();
uriVariables.put("baseHost", (host != null) ? host : "");
// following logic is based on HierarchicalUriComponents#toUriString()
int port = uriComponents.getPort();
uriVariables.put("basePort", (port == -1) ? "" : ":" + port);
String path = uriComponents.getPath();
if (StringUtils.hasLength(path)) {
if (path.charAt(0) != PATH_DELIMITER) {
path = PATH_DELIMITER + path;
}
}
uriVariables.put("basePath", (path != null) ? path : "");
uriVariables.put("baseUrl", uriComponents.toUriString());
uriVariables.put("action", (action != null) ? action : "");
return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri()).buildAndExpand(uriVariables)
.toUriString();
}
}
I'm creating a web application for which I'm using Vue for the frontend and Spring Boot for the backend. Spring Boot serves index.html at / and /index.html, but I want it to be served at other URL's too, for example /account, which in turn will be detected by Vue's Router and will show the proper page.
Additionally, I have some other URL's I don't want to serve index.html. All of them start with /api, meaning that's the place where the Vue app sends requests.
Any help will be appreciated. Thanks.
What you want to do is called an SPA (single page application). In order to achive this you need to do two things:
Tell vue-router to use HTML5 history push: https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations
Tell SpringBoot to serve the index.html when it cannot find a relevant route. Here is a good guide on how to do it using a handler for NoHandlerFoundException: https://medium.com/#kshep92/single-page-applications-with-spring-boot-b64d8d37015d
I have to warn you: when you configure history mode in step 1., click something, it will look like your SPA is already working (no # sign). Beware that this is an illusion. Vue-router tells the browser how the url should look like, but when you refresh the page, the server will return 404. You have to configure step 2 as well.
Because in my application I do not have only VUE in the user interface, redirect all errors to the VUE index.html as is proposed before is not acceptable in my scenario.
Finally, I have solved in another manner using filters ( basically the idea is to intercept all URL that are not css, js, images, etc... used in my VUE UI and take control of the response). In my case the VUE URL starts with "/context/kmobile/":
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
#Component
public class Html5PathFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(Html5PathFilter.class);
// Capture the content of a file from /webapps/kmobile/index.html
// Inspired by https://stackoverflow.com/questions/30431025/spring-how-to-access-contents-of-webapp-resources-in-service-layer
#Value("/kmobile/index.html")
private Resource indexResource;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String path = request.getServletPath();
if (!path.endsWith(".css") && !path.endsWith(".js") && !path.endsWith(".ico") && !path.endsWith(".html") &&
!path.endsWith("/kmobile/")) {
// log.info("YES, do redirect ->" + path);
// Code warning, initially were using redirect, that's a bad practice because from browser get the index.html url what never should be used directly there
// response.sendRedirect(request.getContextPath() + "/kmobile/index.html");
// Disable browser cache
response.setHeader("Expires", "Sat, 6 May 1971 12:00:00 GMT");
response.setHeader("Cache-Control", "must-revalidate");
response.addHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.addHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Pragma", "no-cache");
InputStream is = indexResource.getInputStream();
// Set MIME type
response.setContentType("text/html");
// Content leght
response.setContentLength(is.available());
try (ServletOutputStream out = response.getOutputStream()) {
IOUtils.copy(is, out);
out.flush();
}
return;
} else {
// log.info("NO, do redirect ->" + path);
}
} catch (Exception e) {
log.debug("Error: {}", e.getMessage(), e);
}
//log.info("--> {}", request.getServletPath());
filterChain.doFilter(request, response);
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getServletPath();
boolean valid = path.startsWith("/kmobile");
if (valid) {
log.info("path: {} => {}", path, valid);
}
return !valid;
}
}
I have created an app in the Google Developer's Console, then created OAuth2 credentials. I have a client_id and client_secret. Now, I want to use these to obtain an access token for two-legged calls into the Google Drive API. I am using Google's oauth2 client in java:
import com.google.api.client.auth.oauth2.ClientCredentialsTokenRequest;
import com.google.api.client.auth.oauth2.ClientParametersAuthentication;
import com.google.api.client.auth.oauth2.TokenResponse;
...
public void oauth2Test() {
String clientId = "...";
String clientSecret = "...";
ClientCredentialsTokenRequest request = new ClientCredentialsTokenRequest(
new NetHttpTransport(),
new JacksonFactory(),
new GenericUrl("https://accounts.google.com/o/oauth2/auth"));
request.setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret));
TokenResponse response;
try {
response = request.execute();
} catch (Exception ex) {
ex.printStackTrace();
}
}
However, I get a "400 Bad Request" with message
"Required parameter is missing: response_type".
What is the correct way to obtain an access token in the two-legged request model? Note: I only have the client_id and client_secret, I do not have the full API token.
EDIT: My original question was imprecise. While I prefer to start only with client_id and client_secret, that is not necessary. It is OK to use google-specific APIs to obtain access tokens and it is OK to use GoogleCredential. What is necessary is that I am able to use whatever access token(s) are obtained from the authorization process in a generic REST call. In other words, given google app credentials, which can be {client_id,client_secret}, or a google service account key in either JSON or P12 format, how do I obtain access token(s) and how are they used in the REST API call -- do I set the Authorization header or something else?
The first answer points out that client_credential isn't supported, which I've verified. But I still need a path to get the bearer token, so that I can use it in REST calls without specific Google client API libraries. So I started with code that works, but uses the Google libraries. It requires a JSON credential file.
InputStream is = getClass().getResourceAsStream("JSONCredFile");
GoogleCredential credential = GoogleCredential.fromStream(is).createScoped(scopes);
Drive service = new Drive.Builder(new NetHttpTransport(), new JacksonFactory(), credential)
.setApplicationName("My app")
.build();
FileList result = service.files().list().setPageSize(10)
.setFields("nextPageToken, files(id, name)")
.execute();
By hooking up an SSLSocket proxy to the credential (details omitted), I was able to trace the outbound communication:
POST /token HTTP/1.1
Accept-Encoding: gzip
User-Agent: Google-HTTP-Java-Client/1.23.0 (gzip)
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: oauth2.googleapis.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-Length: 771
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=<lots of encoded stuff>
The reply is a gzip-encoded bearer token, which is used in the API call:
GET /drive/v3/files?fields=nextPageToken,%20files(id,%20name)&pageSize=10 HTTP/1.1
Accept-Encoding: gzip
Authorization: Bearer ya29.c.Eln_BSgrx0afa85mdMstW5jzEvM5dotWpctSXl-DE1jeO2mmu1h0FErr_EZO05YnC-B1yz30IBwOyFXoWr_wwKxlZk08R6eZldNU-EAfrQ1yNftymn_Qqc_pfg
Clearly this is the JWT profile of oauth2. But now what? Somehow I need to get the bearer token without actually making the API call through the specific library. The Google OAuth2 libraries don't seem to support this request type, at least I don't see a "JWT" flavor of TokenRequest. I can cook up the OAuth2 call directly, or create a subclass of TokenRequest that supports JWT?
Any better ideas?
Google doesn't support grant_type=client_credentials which is how you'd do 2LO with an OAuth client ID and secret.
You can use service accounts to do 2LO: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
OK, I finally figured out how to make the JWT, send the OAuth2 request, and extract the access token. For easier integration with the google OAuth2 client, I subclassed TokenRequest:
import com.google.api.client.auth.oauth2.TokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Collection;
import java.util.stream.Collectors;
import org.joda.time.DateTime;
/**
* #author jlilley
*/
public class JWTTokenRequest extends TokenRequest {
private String serviceKeyJson;
private boolean doRsa = true;
public JWTTokenRequest(HttpTransport transport, JsonFactory jsonFactory, GenericUrl tokenServerUrl) {
super(transport, jsonFactory, tokenServerUrl, "urn:ietf:params:oauth:grant-type:jwt-bearer");
}
#Override
public JWTTokenRequest setTokenServerUrl(GenericUrl tokenServerUrl) {
return (JWTTokenRequest)super.setTokenServerUrl(tokenServerUrl);
}
public JWTTokenRequest setServiceKey(String json) throws Exception {
this.serviceKeyJson = json;
return this;
}
public JWTTokenRequest setServiceKey(InputStream is) throws Exception {
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(is))) {
return setServiceKey(buffer.lines().collect(Collectors.joining("\n")));
}
}
#Override
public JWTTokenRequest setScopes(Collection<String> scopes) {
return (JWTTokenRequest) super.setScopes(scopes);
}
#Override
public JWTTokenRequest set(String fieldName, Object value) {
return (JWTTokenRequest) super.set(fieldName, value);
}
/**
* Create a JWT for the given project id, signed with the given RSA key.
*/
private String signJwtRsa(JwtBuilder jwtBuilder, PKCS8EncodedKeySpec spec) {
try {
KeyFactory kf = KeyFactory.getInstance("RSA");
return jwtBuilder.signWith(SignatureAlgorithm.RS256, kf.generatePrivate(spec)).compact();
} catch (Exception ex) {
throw new RuntimeException("Error signing JWT", ex);
}
}
/**
* Create a JWT for the given project id, signed with the given ES key.
*/
private String signJwtEs(JwtBuilder jwtBuilder, PKCS8EncodedKeySpec spec) {
try {
KeyFactory kf = KeyFactory.getInstance("EC");
return jwtBuilder.signWith(SignatureAlgorithm.ES256, kf.generatePrivate(spec)).compact();
} catch (Exception ex) {
throw new RuntimeException("Error signing JWT", ex);
}
}
/**
* Finalize the JWT and set it in the assertion property of the web service call
* #throws java.io.IOException
*/
void makeAssertion() {
JsonParser parser = new JsonParser();
JsonObject jsonDoc = (JsonObject) parser.parse(serviceKeyJson);
String pkStr = jsonDoc.get("private_key").getAsString()
.replace("\n", "")
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "");
byte[] pkBytes = Base64.getDecoder().decode(pkStr);
DateTime now = new DateTime();
JwtBuilder jwtBuilder = Jwts.builder()
.setIssuedAt(now.toDate())
.setExpiration(now.plusMinutes(60).toDate())
.setAudience(getTokenServerUrl().toString())
.setIssuer(jsonDoc.get("client_email").getAsString());
if (getScopes() != null) {
jwtBuilder = jwtBuilder.claim("scope", getScopes());
}
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkBytes);
String pkId = jsonDoc.get("private_key_id").getAsString();
jwtBuilder.setHeaderParam("kid", pkId);
jwtBuilder.setHeaderParam("typ", "JWT");
set("assertion", doRsa ? signJwtRsa(jwtBuilder, spec) : signJwtEs(jwtBuilder, spec));
}
/**
* Finalize the JWT, set it in the assertion property of the web service call, and make the token request.
*/
#Override
public TokenResponse execute() throws IOException {
makeAssertion();
return super.execute();
}
}
Give this, I can set up the token request from the service account JSON key file, execute() it, and fetch the resulting access token. Note that token renewal responsibility is up to the caller.
I am trying to authenticate a user's username and password using an Ajax call.The parameters are passed to java bean and it should return a success or a failure message string to the caller but it shows a Servlet service error.The code is as follows:
package com.eits.portal.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
import com.eits.portal.service.OrgTreeService;
public class UserLoginController extends MultiActionController {
OrgTreeService orgTreeService;
ModelAndView mv;
public String getLoginStatus(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)
throws Exception{
//paramHttpServletResponse.setContentType("application/json");
String userId = paramHttpServletRequest.getParameter("user");
String password = paramHttpServletRequest.getParameter("password");
String s="";
System.out.println("---node"+userId);
System.out.println("---node"+password);
String us="admin";
String pass="admin";
if(userId.equals(us)&& password.equals(pass)){
s="success";
System.out.println("success ");
}else{
s="failure";
System.out.println("unsuccessful");
}
return s;
}
public void setOrgTreeService(OrgTreeService paramOrgTreeService)
{
this.orgTreeService = paramOrgTreeService;
}
}
The code is pretty simple and it does print success on passing the right username and password in the IDE console , but the same string is not passing to the caller function for the front end.Here is a snapshot of the error I am getting :-
http://gyazo.com/12b386a9e586fc0ee5514800c9822071
Is this a right way to pass the success to the caller method , or should i pass an object instead of the string?
Firstly, if you are getting a 404 (Not Found) then you should check that you are calling the correct URL and that your servlet request mapping configuration is specified correctly, e.g. via #RequestMapping annotation if using Spring.
Then, assuming you are making a request similar to below, you can return a Json string from your controller method that will be returned in the response and can be decoded into an object, assuming you may want to pass back more details than simply "success" or "failure".
Ext.Ajax.request({
url: '/someUrl',
params: {
user: 'username',
password: 'password'
},
callback: function(options, success, response) {
if (success) {
var obj = Ext.decode(response.responseText);
....
}
}
});
What is the best approach to catch an exception in a Spring Web Service, extract the details of it, and format it into a soap response? My error message details must go in the header of the Soap response.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ims="http://www.imsglobal.org/services/lis/cmsv1p0/wsdl11/sync/imscms_v1p">
<soapenv:Header>
<imsx_syncResponseHeaderInfo xmlns="http://www.imsglobal.org/services/lis/cmsv1p0/wsdl11/sync/imscms_v1p0">
<imsx_version>V1.0</imsx_version>
<imsx_messageIdentifier>4</imsx_messageIdentifier>
<imsx_statusInfo>
<imsx_codeMajor>failure</imsx_codeMajor>
<imsx_severity>status</imsx_severity>
<imsx_codeMinor>
<imsx_codeMinorField>
<imsx_codeMinorFieldName>TargetEndSystem</imsx_codeMinorFieldName>
<imsx_codeMinorFieldValue>incompletedata</imsx_codeMinorFieldValue>
</imsx_codeMinorField>
</imsx_codeMinor>
</imsx_statusInfo>
</imsx_syncResponseHeaderInfo>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
I do know if it is the best approach but I added a SimpleSoapExceptionResolver object:
import java.util.Date;
import java.util.Locale;
import org.apache.log4j.Logger;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver;
public final class MySimpleSoapExceptionResolver
extends SimpleSoapExceptionResolver {
public MySimpleSoapExceptionResolver () {
super.setOrder(HIGHEST_PRECEDENCE);
}
#Override
protected void customizeFault( final MessageContext messageContext_,
final Object endpoint_,
final Exception exception_,
SoapFault soapFault_) {
WebServiceMessage _webServiceMessageResponse =
messageContext_.getResponse();
SoapMessage _soapMessage = (SoapMessage) _webServiceMessageResponse;
SoapBody _soapBody = _soapMessage.getSoapBody();
String _message = "your error message";
Logger _logger = Logger.getLogger(MySimpleSoapExceptionResolver.class);
_logger.error(_message, exception_);
soapFault_ =
_soapBody.addServerOrReceiverFault(_message, Locale.ENGLISH);
}
}
You can probably implement a interceptor of type org.springframework.ws.server.endpoint.interceptor.EndpointInterceptorAdapter. Register your interceptor in your webservice configuration.
Implement the method handleResponse(MessageContext messageContext, Object endpoint) like this -
handleResponse(MessageContext messageContext, Object endpoint) {
SoapMessage msg = (SoapMessage) messageContext.getResponse();
SoapHeader header = msg.getSoapHeader();
// do what you want to do with header.
}
I have not implemented this but done similar stuff with interceptors in CXF.