Securing Rest Service Resources Using Apache Shiro - java

I'm trying to secure my rest services written using dropwizard by Apache Shiro. First I initialized the security manager in the main method.
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Then I wrote a service for user login.
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
try {
currentUser.login(token);
System.out.println("USER AUTHENTICATED!!!!!!!!");
} catch (Exception uae) {
System.out.println("Error logging in .................");
}
}
Then I declared a method with some java annotations.
#RequiresAuthentication
#RequiresRoles("admin")
#GET
#Path("/account")
#ApiOperation(value = "getAccount")
public void getAccount() {
//do something
}
But when I accessed this resource without logging in, I was successful.
What mistake am I doing? Or should I add something more? Like in the web.xml?

I found this repo very useful. https://github.com/silb/dropwizard-shiro/tree/release-0.2. I followed the instructions given in this. But there is one more thing I added in the configuration file.
#Valid
#JsonProperty("shiro-configuration")
public ShiroConfiguration shiro = new ShiroConfiguration();
Then in the resources class, I wrote login and logout as two services.
#POST
#Path("/session")
#Produces(MediaType.TEXT_PLAIN)
public String login(#FormParam("username") String username, #FormParam("password") String password, #Auth Subject subject) {
subject.login(new UsernamePasswordToken(username, password));
return username;
}
#PUT
#Path("/logout")
#Produces(MediaType.TEXT_PLAIN)
public String logout(#Auth Subject subject){
subject.logout();
return "Successfully logged out!";
}
And then I annotated the secured resources with #RequiresAuthentication annotation.

Related

How do you authenticate to Cisco Contact Center Express Identity Service?

I'm building a 3rd party app to authenticate with Contact Center Express. The documentation is necessary, but insufficient to accomplish this. For example,
https://developer.cisco.com/docs/contact-center-express/#!cisco-identity-service-client-sdk-guide/during-agent-login
// Get Access Token for the received Authorization Code
String redirectURI = config.getRedirectUri();
AccessToken token = client.getAccessToken(authCode, redirectURI);
When and where do you redirect the user to Contact Center to authenticate? I observed that Finesse will redirect the user to
https://contactcenter.example.com:8553/ids/v1/oauth/authorize?redirect_uri=https%3A%2F%2Ffinesse.example.com%3A443%2Fdesktop%2Fsso%2Fauthcode&client_id=8a75xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&state=aHR0cHM6Ly92bS1mLWZpbi1hLmRldi5pbi5zcGluc2NpLmNvbS9kZXNrdG9wL2pfaWRlbnRpdHlfY2hlY2s%2FZXJyb3I9dHJ1ZQlhcHBsb2dpbg%3D%3D&response_type=code
But where is it specified to use the identity service (IDS) path /ids/v1/oauth/authorize? And is state a required parameter? And does the IDS SDK handle the callback path /desktop/sso/authcode? I imagine that it doesn't but what are the parameters that will be sent to it? I'm using Spring framework.
Am I to reverse engineer the whole process, or is there additional documentation that I am missing?
Even after I receive an OAuth token, how would I use it to make other REST calls to other Cisco products? The Finesse REST APIs only mention HTTP basic authentication. There is no mention of headers for "Authorization: Bearer" tokens.
https://developer.cisco.com/docs/finesse/#!sign-in-to-finesse/sign-in-to-finesse
I had to reverse engineer it following all the redirects.
#Controller
public class SSOController {
#Autowired
private IdSClientConfigurationImpl config;
#Autowired
private IdSClient client;
#PostMapping("/login")
public String login(#RequestParam(name="user", required=true) String user) {
// redirect the user to the Cisco Contact Center Express Identity Service
String redirectURI = config.getRedirectUri();
String clientId = config.getClientId();
URI uri = UriComponentsBuilder
.fromUriString("https://contact-center-express:8553/ids/v1/oauth/authorize")
.queryParam("redirect_uri", "{redirect_uri}")
.queryParam("client_id", "{client_id}")
// .queryParam("state", "{state}") // base64 encoded
.queryParam("response_type", "code")
.build(redirectURI, clientId);
return "redirect:"+uri.toString();
}
#GetMapping("/idscallback")
public String idscallback(
#RequestParam(name="code", required=true) String code,
#RequestParam(name="state", required=false) String state,
HttpSession session) throws IdSClientException {
// Get Access Token for the received Authorization Code
String redirectURI = config.getRedirectUri();
AccessToken token = client.getAccessToken(code, redirectURI); // why do I need redirectURI when it's already redirected?
String accessTokenString = token.getAccess_token();
session.setAttribute("token", accessTokenString);
// model.addAttribute("token", accessTokenString);
return "redirect:/";
}
And in a bean far, far away...
#Bean
public IdSClientConfigurationImpl config() throws IOException, IdSClientException {
ClassPathResource idsclientResource = new ClassPathResource("idsclient.properties");
IdSClientConfigurationImpl config = new IdSClientConfigurationImpl(idsclientResource.getFile().getPath());
// IdSClientConfigurationImpl config = new IdSClientConfigurationImpl("src/main/resources/idsclient.properties");
config.load();
return config;
}
#Bean
public IdSClient setupIdsClient() throws IOException, IdSClientException {
IdSClient client = IdSClientFactory.getIdSClient();
client.setTLSContext(createSSLTrustManager(), createHostnameVerifier());
// client.setTLSContext(arg0, arg1) // use secure trust manager and hostname verifier in production
client.init(config);
return client;
}
private X509TrustManager createSSLTrustManager() {
X509TrustManager tm = new TrustAllX509TrustManager();
return tm;
}
private HostnameVerifier createHostnameVerifier() {
HostnameVerifier hv = new SkipAllHostNameVerifier();
return hv;
}

httpcontext in Java RESTful service

I am newbie to Java(came from .Net background), I am trying to write a RESTful service using Jersey framework. I referred this link http://www.vogella.com/tutorials/REST/article.html
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
return "Hello Jersey";
}
Now I want to get the HTTPContext in the above method. Basically I am looking to get the logged-in user name, I hope that should be available using HttpContext.Request.
I am using windows NT authentication.
Can anybody please tell me how to get the HTTPContext/User information inside the Java RESTful service.
You can use below code to get the HTTPContext in your REST resource.
public class RestResource {
#Context
private HttpServletRequest httpServletRequest;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayPlainTextHello() {
RequestContext requestContext = (RequestContext) httpServletRequest.getAttribute(RequestConstants.REQUEST_CONTEXT);
// Get whatever the value set in your request context
String userName = requestContext.requestContext.getQueryString("userName");
return userName;
}
}
let me know if you need any help.
Short answer
The SecurityContext injected with the #Context annotation allows you to access security related information. It has a request scope and hold details about the user who is authenticated.
For other types that can be injected with #Context, check this answer.
Long answer
You must mind that REST authentication must be stateless. That is, you must not rely on sessions stored on server side, hence the user credentials must be sent in each request. In other words, each request must be authenticated/authorized. To understand this concept, this answer may be insightful.
See below how the SecurityContext can be used in a couple of approaches to secure your application:
Basic authentication
The Basic authentication scheme described in the RFC 7617 with HTTPS is a common and efficent approach to secure a REST application:
2. The 'Basic' Authentication Scheme
The Basic authentication scheme is based on the model that the client
needs to authenticate itself with a user-id and a password for each
protection space ("realm"). [...] The server will service the request only if it can validate
the user-id and password for the protection space applying to the
requested resource.
[...]
To receive authorization, the client
obtains the user-id and password from the user,
constructs the user-pass by concatenating the user-id, a single
colon (:) character, and the password,
encodes the user-pass into an octet sequence,
and obtains the basic-credentials by encoding this octet sequence
using Base64 into a sequence of US-ASCII
characters.
[...]
If the user agent wishes to send the user-id "Aladdin" and password
"open sesame", it would use the following header field:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
[...]
You could use a ContainerRequestFilter to extract the user credentials from the Authorization request header, authenticate them against your authentication provider and then set a SecurityContext with the username for the request. The AuthenticationService implementation is up to you:
#Provider
#Priority(Priorities.AUTHENTICATION)
class AuthenticationFilter implements ContainerRequestFilter {
#Inject
private AuthenticationService authenticationService;
#Override
public void filter(ContainerRequestFilter requestContext) {
// Get the HTTP Authorization header from the request
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Basic ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the Basic authentication token from the HTTP Authorization header
String token = authorizationHeader.substring("Basic".length()).trim();
// Decodes the token
String credentials = Base64.getDecoder().decode(token);
String[] split = decoded.split(":");
try {
// Authenticate the credentials against in your authentication provider
authenticationService.authenticate(split[0], split[1]);
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
// Updates the security context for the request
SecurityContext securityContext = requestContext.getSecurityContext();
requestContext.setSecurityContext(
new CustomSecurityContext(split[0], securityContext.isSecure()));
}
}
A custom SecurityContext implementation could be like:
public class CustomSecurityContext implements SecurityContext {
private final String username;
private final boolean secure;
public BasicSecurityContext(String username, boolean secure) {
this.username = username;
this.secure = secure;
}
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return username;
}
};
}
#Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
#Override
public boolean isSecure() {
return secure;
}
#Override
public boolean isUserInRole(String role) {
return true;
}
}
Then the SecurityContext can be injected in any resource class using the #Context annotation:
#Path("/example")
public class MyResource {
#Context
private SecurityContext securityContext;
...
}
It also can be injected in a resource method parameter:
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response myResourceMethod(#PathParam("id") Long id,
#Context SecurityContext securityContext) {
...
}
And then get the Principal from the SecurityContext:
Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
Token based authentication
In an authentication scheme based on tokens, the token becomes a credential of the user.
Hard credentials such as username and password are exchanged for a token that must be sent in each request then the server can perform authentication/authorization. Tokens can be valid for a short amount of time, can be revoked, can carry scope details (what can be requested with the token), etc.
For more details, have a look at this answer. The SecurityContext can be used in the same way as explained above.

Trying to use Feign to log into Spring OAuth2 server

I'm looking to make a grant_type = password login from a service with Feign to the Spring OAuth2 server. I've confirmed the OAuth2 server works when I make a normal REST call. But when trying with Feign, it fails.
In my service I have:
#Component
#Scope("prototype")
#FeignClient(url = "http://localhost:9999/uaa")
public interface AuthClient {
#RequestMapping(value="/oauth/token", method=RequestMethod.POST)
#Headers({"Authorization: Basic some_base64encoded_client_secret_here=="})
public LoginResponse login(
#Param("username") String username, #Param("password") String password, #Param("grant_type") String grantType);
}
I get this error:
java.lang.ClassCastException: Cannot cast com.sun.proxy.$Proxy129 to org.springframework.web.bind.annotation.RequestMapping
Most examples I find show how to intercept Feign to use OAuth headers/etc. and assume the access token already exists. But this is not my issue. I don't have an access token yet because I'm trying to get the access token from logging in. Any ideas on how I can log in using Feign?
You can map your own response like this:
#Component
#FeignClient(name = "authClient", url = "http://localhost:8080")
public interface AuthClient {
#RequestMapping(value="/oauth/token", method= RequestMethod.POST)
AuthResponse login(#RequestParam("grant_type") String grantType, #RequestParam("username") String username,
#RequestParam("password") String password, #RequestParam("scope") String scope);
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
class AuthResponse {
String access_token;
String token_type;
String refresh_token;
String expires_in;
}
}
public class AuthService {
private final AuthClient authClient;
public AuthService(AuthClient authClient) {
this.authClient = authClient;
}
public AuthClient.AuthResponse authenticate(String login, String password, String scopes) {
return this.authClient.login("password", "admin", "admin", "read+write");
}
}
You can register a request interceptor to add Authorization headers at every request:
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("clientId", "clientSecret");
}
This code works with a sample Spring Security OAuth2 server.

Custom weblogic application SSO authentication

Can anybody describe me how to use single sign-on SAML authentication in my weblogic application?
I can't use default SSO configuration because I need to do some custom checks before authenticate user. Now I use custom form auth:
public String login() {
//Some my checks...
HttpServletRequest request =
(HttpServletRequest)((ServletRequest)ADFContext.getCurrent().getEnvironment().getRequest());
CallbackHandler handler =
new SimpleCallbackHandler(username, password);
Subject subject;
try {
subject = Authentication.login(handler);
ServletAuthentication.runAs(subject, request);
ServletAuthentication.generateNewSessionID(request);
} catch (LoginException e) {
handleException(e, "Введен некорректный логин или пароль");
return "fail";
}
//Some other checks
return "success"
}
I would like to write something like this:
public String login() {
if (isSSOEnabled) {
//DO SOMETHING TO USE SSO
} else {
//do form-based auth
}
}
I use weblogic 10.3.6 and AD with Kerberos.

Jersey Restful API Validation

how can i validate user name and password using jersey restful API.
Here is the code Below:
I tried using the HttpRequest and HttpResponse
#POST
#Path("/login")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public String postLoginJson(#FormParam("userName") String userName,
#FormParam("password") String password) {
String user = null;
try {
user = userObjectJson();
} catch (JSONException e) {
e.printStackTrace();
}
System.out.println("User Name = " + userName);
System.out.println("Password = " + password);
return user;
}
Security is best handled as an aspect independent of your REST resources.
I suggest letting your hosting container or context handle this. Your resources can access this information, as provided by the container.
For information on how to configure your application using JavaEE realms, see https://jersey.java.net/documentation/latest/security.html
For using Spring as a security solution, see
http://projects.spring.io/spring-security/

Categories

Resources