swagger securityDefinition with Resteasy - token

I did configure swagger with an Application subclass and the beanConfig object, my securityDefinition must allow swagger ui to show de api_key field to allow authentication for all my services layer.
BeanConfig beanConfig = new BeanConfig();
beanConfig.setSchemes(new String[] { "http" });
beanConfig.setHost("192.168.4.9:8080");
beanConfig.setBasePath("/cjppa/rest");
beanConfig.setResourcePackage("com.cjppa.fpuna.backend.resources");
beanConfig.setScan(true);
beanConfig.setPrettyPrint(true);
io.swagger.models.Info info = new io.swagger.models.Info();
io.swagger.models.Contact contact = new io.swagger.models.Contact();
info.setVersion("1.0");
beanConfig.setInfo(info);
io.swagger.models.auth.ApiKeyAuthDefinition apikey = new
io.swagger.models.auth.ApiKeyAuthDefinition();
apikey.setName("x-token");
apikey.setIn(In.HEADER);
Swagger swagger = new Swagger().info(info);
swagger.securityDefinition("api_key", apikey);
beanConfig.configure(swagger);
the expected api_key comes in the "x-token" http header

I tried also to bring swagger into my resteasy webservice with using BasicAuthentification for some operations of my webservice. I imported swagger via maven in my pom.xml:
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.18</version>
</dependency>
In my Application class I configured the BeanConfig:
import javax.ws.rs.ApplicationPath;
import io.swagger.jaxrs.config.BeanConfig;
#ApplicationPath("/rest")
public class Application extends javax.ws.rs.core.Application{
public Application() {
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0");
beanConfig.setResourcePackage("de.mycompany.topic.ws");
beanConfig.setBasePath("/de.mycompany.topic.ws/rest/");
beanConfig.setScan(true);
}
}
The important thing is to configure the BasicAuthentification in an ReaderListener implementation via Annotations. basicAuth is an arbitrary name.
import io.swagger.annotations.BasicAuthDefinition;
import io.swagger.annotations.SecurityDefinition;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.jaxrs.Reader;
import io.swagger.jaxrs.config.ReaderListener;
import io.swagger.models.Swagger;
#SwaggerDefinition(securityDefinition = #SecurityDefinition(basicAuthDefinitions = {
#BasicAuthDefinition(key = "basicAuth")
}) )
public class SwaggerCustomizeDefinition implements ReaderListener {
#Override
public void beforeScan(Reader reader, Swagger swagger) {
}
#Override
public void afterScan(Reader reader, Swagger swagger) {
}
}
In MyRestService I annotate my operations that should be not usable without basic authentification. See here e.g. for saving customers:
#Api
#Path("/")
public class MyRestService {
private final static String UTF8 = ";charset=UTF-8";
#POST
#Path("/customer")
#Produces(MediaType.APPLICATION_JSON + UTF8)
#ApiOperation(
value = "Saves customer specified in the body",
notes = "note that appears in swagger ui",
authorizations = {
#Authorization(value = "basicAuth", scopes={})
})
#ApiResponses(value = {
#ApiResponse(code = 201, message = "customer created"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 404, message = "customer format not supported"),
})
public Response saveCustomer(
String content,
#BasicAuthDefinition(key = "basicAuth") #HeaderParam("Authorization") String authorization) {
// authorization
try {
if (!MyManager.isAuthorized(authorization)) {
return Response.status(Status.UNAUTHORIZED).build();
}
} catch (Exception e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
//do the work, authorization was ok
}
}
That's it. I tried a lot of variations and this was the only one that works for me in whole. My main problem was, that the authorize button not appears in the ui and the lock above the single methods in the swagger ui was not clickable so that the basic authentification modal dialog not appears. With this implementation it works.

you can implements io.swagger.jaxrs.config.ReaderListener ,addSecurity in afterScan method . eg:
#SwaggerDefinition(securityDefinition = #SecurityDefinition(apiKeyAuthDefinitions = {
#ApiKeyAuthDefinition(in = ApiKeyAuthDefinition.ApiKeyLocation.HEADER, key = "token", name = "E-token"),
#ApiKeyAuthDefinition(in = ApiKeyAuthDefinition.ApiKeyLocation.HEADER, key = "userId", name = "E-userId"),
#ApiKeyAuthDefinition(in = ApiKeyAuthDefinition.ApiKeyLocation.HEADER, key = "corpId", name = "E-corpId") }) )
public class SwaggerCustomizeDefinition implements ReaderListener {
#Override
public void beforeScan(Reader reader, Swagger swagger) {
}
#Override
public void afterScan(Reader reader, Swagger swagger) {
swagger.addSecurity(new SecurityRequirement().requirement("token"));
swagger.addSecurity(new SecurityRequirement().requirement("userId"));
swagger.addSecurity(new SecurityRequirement().requirement("corpId"));
}
}

Related

How to make SpringDoc/Swagger detect beanMethod from abstract superclass

I am having issues with Spring-doc / Swagger while using Spring Boot WebFlux with the functional approach (RouterFunction and Handler).
pom.xml:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-core</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webflux-ui</artifactId>
<version>1.6.4</version>
</dependency>
End points in router config:
#Bean
#ParticipantDoc
public RouterFunction<ServerResponse> participantRoutes() {
return RouterFunctions
.route(GET("/participants/{publicId}"), participantHandler::byPublicId)
.andRoute(POST("/participants"), participantHandler::create)
.andRoute(PUT("/participants"), participantHandler::update)
.andRoute(DELETE("/participants/{publicId}"), participantHandler::delete);
}
Sample open api doc annotation for single operation (#ParticipantDoc):
#RouterOperation(
path = "/participants/{publicId}",
produces = { MediaType.APPLICATION_JSON_VALUE },
method = RequestMethod.GET,
operation = #Operation(
tags = "Participant",
operationId = "getParticipantByPublicId",
responses = {
#ApiResponse(
responseCode = "200",
description = "Successful operation",
content = #Content(
schema = #Schema(implementation = ParticipantModel.class))),
#ApiResponse(responseCode = "404", description = "Participant not found")
},
parameters = { #Parameter(in = ParameterIn.PATH, name = "publicId") })),
The superclass with the base methods:
#RequiredArgsConstructor
public abstract class AbstractHandler<E extends BaseEntity, ID extends Serializable, M extends BaseModel, S extends AbstractService<E, ID, M>> {
private static final String idTemplate = "publicId";
private final S service;
private final M model;
public Mono<ServerResponse> byPublicId(ServerRequest req) {
var id = req.pathVariable(idTemplate);
return ServerResponse.ok().body(service.findOne(id),
model.getClass())
.onErrorResume(e -> {
return Mono.error(e);
});
}
// other methods
}
ParticipantHandler which extends the AbstractHandler:
#Component
public class ParticipantHandler extends AbstractHandler<Participant, Long, ParticipantModel, ParticipantService> {
public ParticipantHandler(ParticipantService service) {
super(service, new ParticipantModel());
}
}
Before using this approach, each method was coded inside the ParticipantHandler class (No inheritance) and I was able to test the end points using Swagger-UI by defining beanClass and beanMethod parameters.
When these parameters are set while on the new implementaion, no end points are displayed on the Swagger-UI page. On the otherhand if I ignore these two parameters, the testing functionality on Swagger-UI doesn't work.
I would like to know if there is a solutution for this.

Swagger endless loading when i click in Execute

I am using Swagger with keycloak. I am getting the below error when I click on Execute button.
when I click on Execute on swagger I see the loading image, and there is no any request on the network tap because I have an error on the console.
I will add my config code and YML file to allow you to see what I do.
anyone can help me, please?
Here is the output in the console:
Here is the error code in the console tap (Its generated code):
here is the swagger UI page:
here is the swagger config:
#Slf4j
#Configuration
#TbCoreComponent
public class SwaggerConfiguration {
#Value("${swagger.api_path_regex}")
private String apiPathRegex;
#Value("${swagger.security_path_regex}")
private String securityPathRegex;
#Value("${swagger.non_security_path_regex}")
private String nonSecurityPathRegex;
#Value("${swagger.title}")
private String title;
#Value("${swagger.description}")
private String description;
#Value("${swagger.contact.name}")
private String contactName;
#Value("${swagger.contact.url}")
private String contactUrl;
#Value("${swagger.contact.email}")
private String contactEmail;
#Value("${swagger.version}")
private String version;
#Value("${app.version:unknown}")
private String appVersion;
// Used to get token from Keyclaok
#Value("${swagger.auth.token_url}")
private String keycloakAuthTokenUrl;
#Value("${security.keycloak.realm}")
private String keycloakRealm;
#Value("${security.keycloak.clientId}")
private String keyclaokAuthCliendId;
#Bean
public Docket yousefApi() {
TypeResolver typeResolver = new TypeResolver();
return new Docket(DocumentationType.SWAGGER_2)
.groupName("Hammad")
.apiInfo(apiInfo())
.additionalModels(
typeResolver.resolve(ThingsboardErrorResponse.class),
typeResolver.resolve(ThingsboardCredentialsExpiredResponse.class)
)
.select()
.paths(apiPaths())
.paths(any())
.build()
.globalResponses(HttpMethod.GET, defaultErrorResponses(false))
.globalResponses(HttpMethod.POST, defaultErrorResponses(true))
.globalResponses(HttpMethod.DELETE, defaultErrorResponses(false))
.securitySchemes(newArrayList(apiKey()))
.securityContexts(newArrayList(securityContext()))
.enableUrlTemplating(true);
}
#Bean
#Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
ApiListingBuilderPlugin loginEndpointListingBuilder() {
return new ApiListingBuilderPlugin() {
#Override
public void apply(ApiListingContext apiListingContext) {
if (apiListingContext.getResourceGroup().getGroupName().equals("Hammad")) {
ApiListing apiListing = apiListingContext.apiListingBuilder().build();
if (apiListing.getResourcePath().equals(keycloakAuthTokenUrl)) {
apiListingContext.apiListingBuilder().tags(Set.of(new Tag("login-endpoint", "Login Endpoint")));
apiListingContext.apiListingBuilder().description("Login Endpoint");
}
}
}
#Override
public boolean supports(#NotNull DocumentationType delimiter) {
return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
}
};
}
#Bean
UiConfiguration uiConfig() {
return UiConfigurationBuilder.builder()
.deepLinking(true)
.displayOperationId(false)
.defaultModelsExpandDepth(1)
.defaultModelExpandDepth(1)
.defaultModelRendering(ModelRendering.EXAMPLE)
.displayRequestDuration(false)
.docExpansion(DocExpansion.NONE)
.filter(false)
.maxDisplayedTags(null)
.operationsSorter(OperationsSorter.ALPHA)
.showExtensions(false)
.showCommonExtensions(false)
.supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS)
.validatorUrl(null)
.persistAuthorization(true)
.syntaxHighlightActivate(true)
.syntaxHighlightTheme("agate")
.build();
}
private ApiKey apiKey() {
return new ApiKey("Bearer", "X-Authorization", "header");
}
private OAuth securityScheme() {
List<GrantType> grantTypes = newArrayList(new ResourceOwnerPasswordCredentialsGrant(keycloakAuthTokenUrl));
return new OAuth("KeycloakAuth", new ArrayList<>(), grantTypes);
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(securityPathOperationSelector())
.build();
}
private Predicate<String> apiPaths() {
return regex(apiPathRegex);
}
private Predicate<OperationContext> securityPathOperationSelector() {
return new SecurityPathOperationSelector(securityPathRegex, nonSecurityPathRegex);
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] authorizationScopes = new AuthorizationScope[3];
authorizationScopes[0] = new AuthorizationScope(Authority.SYS_ADMIN.name(), "System administrator");
authorizationScopes[1] = new AuthorizationScope(Authority.TENANT_ADMIN.name(), "Tenant administrator");
authorizationScopes[2] = new AuthorizationScope(Authority.CUSTOMER_USER.name(), "Customer");
return authorizationScopes;
}
List<SecurityReference> defaultAuth() {
return newArrayList(new SecurityReference("KeycloakAuth", scopes()), new SecurityReference("Bearer", scopes()));
}
private ApiInfo apiInfo() {
String apiVersion = version;
if (StringUtils.isEmpty(apiVersion)) {
apiVersion = appVersion;
}
return new ApiInfoBuilder()
.title(title)
.description(description)
.contact(new Contact(contactName, contactUrl, contactEmail))
.version(apiVersion)
.build();
}
/** Helper methods **/
private List<Response> defaultErrorResponses(boolean isPost) {
return List.of(
errorResponse("400", "Bad Request",
ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST)),
errorResponse("401", "Unauthorized",
ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
errorResponse("403", "Forbidden",
ThingsboardErrorResponse.of("You don't have permission to perform this operation!",
ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)),
errorResponse("404", "Not Found",
ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND)),
errorResponse("429", "Too Many Requests",
ThingsboardErrorResponse.of("Too many requests for current tenant!",
ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))
);
}
private Response errorResponse(String code, String description, ThingsboardErrorResponse example) {
return errorResponse(code, description, List.of(errorExample("error-code-" + code, description, example)));
}
private Response errorResponse(String code, String description, List<Example> examples) {
return errorResponse(code, description, examples, ThingsboardErrorResponse.class);
}
private Response errorResponse(String code, String description, List<Example> examples,
Class<? extends ThingsboardErrorResponse> errorResponseClass) {
return new ResponseBuilder()
.code(code)
.description(description)
.examples(examples)
.representation(MediaType.APPLICATION_JSON)
.apply(classRepresentation(errorResponseClass, true))
.build();
}
private Example errorExample(String id, String summary, ThingsboardErrorResponse example) {
return new ExampleBuilder()
.mediaType(MediaType.APPLICATION_JSON_VALUE)
.summary(summary)
.id(id)
.value(example).build();
}
private Consumer<RepresentationBuilder> classRepresentation(Class<?> clazz, boolean isResponse) {
return r -> r.model(
m ->
m.referenceModel(ref ->
ref.key(k ->
k.qualifiedModelName(q ->
q.namespace(clazz.getPackageName())
.name(clazz.getSimpleName())).isResponse(isResponse)))
);
}
private static class SecurityPathOperationSelector implements Predicate<OperationContext> {
private final Predicate<String> securityPathSelector;
SecurityPathOperationSelector(String securityPathRegex, String nonSecurityPathRegex) {
this.securityPathSelector = (not(regex(nonSecurityPathRegex)));
}
#Override
public boolean test(OperationContext operationContext) {
return this.securityPathSelector.test(operationContext.requestMappingPattern());
}
}
}
here is the YML file:
swagger:
auth:
token_url: ${security.keycloak.serverUrl}/realms/${security.keycloak.realm}/protocol/openid-connect/token/
auth_url: ${security.keycloak.serverUrl}/realms/${security.keycloak.realm}/protocol/openid-connect/auth/
api_path_regex: "${SWAGGER_API_PATH_REGEX:/api/(customer|device|user|tenant).*}"
security_path_regex: "${SWAGGER_SECURITY_PATH_REGEX:/api/(customer|device|user|tenant).*}"
non_security_path_regex: "${SWAGGER_NON_SECURITY_PATH_REGEX:/api/(?:noauth|v1)/.*}"
title: "${SWAGGER_TITLE:yousefCo REST API}"
description: "${SWAGGER_DESCRIPTION: yousefCo open-source IoT platform REST API documentation.}"
contact:
name: "${SWAGGER_CONTACT_NAME:yousefCo Team}"
url: "${SWAGGER_CONTACT_URL:http://iot.test.net}"
email: "${SWAGGER_CONTACT_EMAIL:info#gmail.com}"
license:
title: "${SWAGGER_LICENSE_TITLE:Apache License Version 2.0}"
url: "${SWAGGER_LICENSE_URL:https://github.com/yousef/yousef/blob/master/LICENSE}"
version: "${SWAGGER_VERSION:}"
I think you have mixed the configuration between swagger version 2 and 3, your overall setting is configured to be used in Swagger 3.0, so
First, You have to change the DocumentationType to OAS_30.
Second, When using the ApiKey security scheme, note that the field name will be used as the header when calling an API, so you have to change it to:
private ApiKey apiKey() {
return new ApiKey("X-Authorization", "AnyNameYouWant", "header");
}
Note that you have to add the Bearer word before the token, also you have to change the name SecurityReference to X-Authorization.
Third, If you need to config the OAuth2 security scheme, instead of using the OAuth class, use the OAuth2Scheme builder class with password flow, like this:
return OAuth2Scheme.OAUTH2_PASSWORD_FLOW_BUILDER
.name("KeycloakAuth")
.scopes(newArrayList(scopes()))
.tokenUrl(keycloakAuthTokenUrl)
.refreshUrl(keycloakAuthTokenUrl)
.build();
Don't forget to add the same scopes in SecurityReference into the OAuth2Scheme scopes.

SpringDoc OIDC: how to show only Implicit Flow among the available authorizations?

I'm trying to configure SpringDoc/Swagger-UI in order to show only the Implicit Flow
when clicking on the Authorize button.
However, it shows all the possible authorization methods supported by the IDAM,
as show at /.well-known/openid-configuration:
"grant_types_supported":["authorization_code","implicit","refresh_token","password","client_credentials","urn:ietf:params:oauth:grant-type:device_code","urn:openid:params:grant-type:ciba"]
authorization_code
implicit
refresh_token
password
client_credentials
urn:ietf:params:oauth:grant-type:device_code
urn:openid:params:grant-type:ciba
This is my current configuration:
#Configuration
#RequiredArgsConstructor
public class OpenAPIConfiguration {
private final OAuth2Configuration oAuth2Configuration;
#Bean
public SecurityScheme securityScheme() {
String tokenIssuer = this.oAuth2Configuration.getIssuers().get(0);
String openIdConnectUrl = tokenIssuer + "/.well-known/openid-configuration";
OAuthFlow implicitOAuthFlow = new OAuthFlow();
return new SecurityScheme()
.name("OIDC-Auth")
.type(SecurityScheme.Type.OPENIDCONNECT)
.scheme("bearer")
.bearerFormat("jwt")
.in(SecurityScheme.In.HEADER)
.openIdConnectUrl(openIdConnectUrl)
.flows(new OAuthFlows().implicit(implicitOAuthFlow));
}
#Bean
public SecurityRequirement securityRequirement() {
return new SecurityRequirement().addList("OIDC-Auth");
}
#Bean
public OpenAPI openAPI(SecurityScheme securityScheme, SecurityRequirement securityRequirement) {
return new OpenAPI()
.info(new Info()
.title("MY API")
.version("1"))
.components(new Components()
.addSecuritySchemes(securityScheme.getName(), securityScheme))
.addSecurityItem(securityRequirement);
}
}
How can I limit the flows to be displayed on the UI?
The example below works for me:
...
private static final String PROTOCOL_OPENID_CONNECT = "%s/realms/%s/protocol/openid-connect";
#Bean
OpenAPI customOpenApi() {
return new OpenAPI()
.addServersItem(new Server().url(API_SERVER_URL))
.components(createOauth2SecurityScheme())
.security(createOauth2SecurityRequirement())
.info(createInfo());
}
private Components createOauth2SecurityScheme() {
return new Components().addSecuritySchemes("oAuth2", createOauth2Scheme());
}
private List<SecurityRequirement> createOauth2SecurityRequirement() {
return List.of(new SecurityRequirement().addList("oAuth2"));
}
private SecurityScheme createOauth2Scheme() {
String authUrl = String.format(PROTOCOL_OPENID_CONNECT, AUTH_SERVER_URL, REALM);
String tokenUrl = String.format(PROTOCOL_OPENID_CONNECT, AUTH_SERVER_URL, REALM);
return new SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.description("OAuth2 Flow")
.flows(new OAuthFlows()
.authorizationCode(
new OAuthFlow()
.authorizationUrl(authUrl + "/auth")
.tokenUrl(tokenUrl + "/token")
.scopes(new Scopes())
));
}
...

Swagger Authentication using Annotation

I am trying to have a basic Auth in my swagger ui.I am using Swagger 2.0.5 as a maven library.Using SwaggerConfig class to create docket Api and other configuration.
`
public class SwaggerConfig {
/**
* Every Docket bean is picked up by the swagger-mvc framework - allowing for multiple
* swagger groups i.e. same code base multiple swagger resource listings.
*/
#Bean
public Docket api() {
AuthorizationScope[] authScopes = new AuthorizationScope[1];
authScopes[0] = new AuthorizationScopeBuilder()
.scope("")
.build();
SecurityReference securityReference = SecurityReference.builder()
.reference("basicAuth")
.scopes(authScopes)
.build();
ArrayList<SecurityReference> reference = new ArrayList<SecurityReference>(1);
reference.add(securityReference);
ArrayList<SecurityContext> securityContexts = new ArrayList<SecurityContext>(1);
securityContexts.add(SecurityContext.builder().securityReferences(reference).build());
ArrayList<SecurityScheme> auth = new ArrayList<SecurityScheme>(1);
auth.add(new BasicAuth("basicAuth"));
Documentation Doc = new DocumentationBuilder().basePath("/swagger-ui.html").build();
return new Docket(DocumentationType.SWAGGER_2)
.securitySchemes(auth)
.securityContexts(securityContexts)
.select()
.apis(RequestHandlerSelectors.basePackage("com.toyota.tme.consumerapi.rest"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
/* #Bean
public Docket customDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.regex("/v1/.*"))
.build();
}*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Consumer API")
.description("Azure based Consumer API.")
.contact("CarIT")
.build();
}
}`
My Issue is,I am using authorization annotatation in my rest service to enable Basic Auth.
#Api(value = "/ping", tags = "1.Ping", description = "API",authorizations = {#Authorization(value="basicAuth")})
#RestController
#RequestMapping("/api")
public class ping {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#RequestMapping(path = "/ping", method = RequestMethod.GET,
produces = MediaType.TEXT_HTML_VALUE)
#ApiOperation(value = "Ping service", httpMethod = "GET", response = String.class,
produces = "text/html", notes = "ping service",authorizations = {#Authorization(value="basicAuth")})
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", response = String.class),
#ApiResponse(code = 400, message = "Bad request"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 409, message = "Conflict"),
#ApiResponse(code = 503, message = "Dependent System(s) are unavailable"),
#ApiResponse(code = 500, message = "Unknown server Error occurred")})
public ResponseEntity<String> ping() {
this.logger.info("springframework api :Ping Request received for ConsumerAPI");
return new ResponseEntity<>(ApplicationConstants.Ping, HttpStatus.OK);
}
}
But this code is not working.I am not able to see any authorization asked by Swagger.Please need a help in this
Same for me. Solved this by moving the value authorizations from Api to ApiOperation annotation. Not beautiful but working.

How to use open Id in jsp

Hi I am trying to build a login system like Stack Overflow but not find the right way to do this in JSP. I am working in struts2.
The following illustrate Single Sign On (SSO) using Oauth, for which you can create a SSO system similar to that of Stack Overflow.
Use scribe: https://github.com/fernandezpablo85/scribe-java/wiki/getting-started
The following example will demonstrate using Twitter...
1) Demonstrate an action to get twitter credentials.
package com.quaternion.struts2basic.action;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.TwitterApi;
import org.scribe.model.Token;
import org.scribe.oauth.OAuthService;
#Results(value = {
#Result(name = "success", location = "${authorizationURL}", type = "redirect"),
#Result(name = "error", location = "/WEB-INF/content/error.jsp")
})
public class TwitterGrantAccess extends ActionSupport implements SessionAware {
private Map<String, Object> session;
private String authorizationURL = null;
#Override
public String execute() {
//Twitter twitter = new TwitterFactory().getInstance();
String consumer_key = "rUPV8tpIcFtyMeSDlnzclA";
String consumer_secret = "16omdjNoEYgwoXfZMc0XrXSxiHDaS0UZUxQzWhTFg";
OAuthService twitterService = new ServiceBuilder()
.provider(TwitterApi.class)
.apiKey(consumer_key)
.apiSecret(consumer_secret)
.callback("http://127.0.0.1:8080/Struts2Basic/twitter-callback")
.build();
Token requestToken = twitterService.getRequestToken();
authorizationURL = twitterService.getAuthorizationUrl(requestToken);
session.put("twitterService", twitterService);
session.put("requestToken", requestToken);
return SUCCESS;
}
public String getAuthorizationURL() {
return this.authorizationURL;
}
#Override
public void setSession(Map<String, Object> map) {
this.session = map;
}
}
2) Action which twitter redirects back to...
package com.quaternion.struts2basic.action;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
#Results(value = {
#Result(name = "success", location = "/WEB-INF/content/twitter-callback-success.jsp"),
#Result(name = "error", location = "/WEB-INF/content/error.jsp")
})
public class TwitterCallback extends ActionSupport implements SessionAware {
private Map<String, Object> session;
private String key;
private String secret;
//returned from twitter
private String oauth_token;
private String oauth_verifier;
#Override
public String execute() {
if (session.containsKey("accessToken") && session.get("accessToken") != null) {
return SUCCESS; //accessToken already exists!
}
Token requestToken = (Token) session.get("requestToken");
if (requestToken == null) {
super.addActionError("requestToken is null");
return ERROR;
}
OAuthService twitterService = (OAuthService) session.get("twitterService");
System.out.println(requestToken.toString());
System.out.println(this.getOauth_verifier());
//Token accessToken = twitter.getOAuthAccessToken(requestToken, this.getOauth_verifier());
Token accessToken = twitterService.getAccessToken(requestToken, new Verifier(this.getOauth_verifier()));
session.put("accessToken", accessToken);
this.setKey(accessToken.getToken()); //just to see something happen
this.setSecret(accessToken.getSecret());//just to see something happen
return SUCCESS;
}
#Override
public void setSession(Map<String, Object> map) {
this.session = map;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public String getOauth_token() {
return oauth_token;
}
public void setOauth_token(String oauth_token) {
this.oauth_token = oauth_token;
}
public String getOauth_verifier() {
return oauth_verifier;
}
public void setOauth_verifier(String oauth_verifier) {
this.oauth_verifier = oauth_verifier;
}
}
I'll omit the views because they really don't do anything
3) An action which writes "Hello from Struts2!", which isn't very good because twitter will only let you run this once and because the status is the same will not let you post it again... but it gets the process across. After updating the status it redirects to your twitter page, if you change the "YOUR_USER_NAME" part of the url in the redirect of course.
package com.quaternion.struts2basic.action;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
import org.apache.struts2.interceptor.SessionAware;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.oauth.OAuthService;
#Results({
#Result(name = "success", location = "https://twitter.com/#!/YOUR_USER_NAME", type = "redirect")
})
public class Tweet extends ActionSupport implements SessionAware {
private Map<String, Object> session;
private String status;
#Override
public String execute() {
Token accessToken = (Token) session.get("accessToken");
OAuthService twitterService = (OAuthService) session.get("twitterService");
String url = "http://api.twitter.com/1/statuses/update.json?status=";
String twitterStatus = "hello!";
OAuthRequest request = new OAuthRequest(Verb.POST, url + twitterStatus);
twitterService.signRequest(accessToken, request);
Response response = request.send();
return SUCCESS;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatus() {
return this.status;
}
#Override
public void setSession(Map<String, Object> map) {
session = map;
}
}
That is pretty much it. The nice things about scribe is it is so easy to configure for the different providers (for basic authentication, using their APIs after is another matter and that is up to you).
It dependents upon how you want to build it.There are certain number of library which you can use to build you login system and few of them are
Joid
openid4java
Here is a outline what all you have to do in order to make the complete flow
Create a JSP page where use can select a way to choose his login system.
Call an action class which Create an authentication request for this identifier.
Redirect user to the OpenId service provider and let him authorize themself.
Receive the OpenID Provider's authentication response at your callback action.
parse the response if you need to store some information.

Resources