I'm trying to write a code that will open multiple URLs that require authentication and parse the text of the webpage for a SUCCESS message. If I supply the username and password in the authentication pop up, then the rest will go through without having to supply authentication every time. Right now, my authentication class looks like so:
public class CustomAuthenticator extends Authenticator {
protected PasswordAuthentication getPasswordAuthentication() {
String username = "********";
String password = "********";
return new PasswordAuthentication(username, password.toCharArray());
}
}
And my URL parsing method is:
public static String urlSuccessUnknown(String url) {
Document doc;
String res = null;
Authenticator.setDefault(new CustomAuthenticator());
try {
doc = Jsoup.connect(url).get();
res = doc.body().text();
if(res.indexOf("SUCCESS") >= 0)
return "SUCCESS";
else if(!(res.indexOf("SUCCESS") >= 0))
return "FLAG";
}
catch (Exception e) { e.printStackTrace(); }
return "Unable to correctly parse";
}
Whenever I test this with one URL, I receive a "java.lang.NullPointerException" at the line "res = doc.body().text();" and two other lines in other parts of my code. What am I doing wrong?
Also, when the webpage is opened, it asks for the username and password in a pop-up window instead of embedded in the page. I just wanted to include this in case a CustomAuthenticator is not the correct way to supply the username and password. Thank you for your time.
Related
I´m currently messing around with JAX-RS specifically Resteasy, because it "just works" with Wildfly and I don´t have to configure anything. That´s really the only reason I use that.
I did already implement Basic Authentication, looking forward to replacing it with OAuth2 later, just did this now for simplicity reasons.
The ContainerRequestFilter looks like this
#Provider
public class SecurityFilter implements ContainerRequestFilter {
private static final String AUTHORIZATION_HEADER_KEY = "Authorization";
private static final String AUTHORIZATION_HEADER_PREFIX = "Basic ";
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
if(isAuthenticated(containerRequestContext) == false)
containerRequestContext.abortWith(createUnauthorizedResponse("Access denied."));
}
private boolean isAuthenticated(ContainerRequestContext containerRequestContext) {
List<String> authHeader = containerRequestContext.getHeaders().get(AUTHORIZATION_HEADER_KEY);
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) containerRequestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
if (authHeader != null && authHeader.size() > 0) {
String authToken = authHeader.get(0).replaceFirst(AUTHORIZATION_HEADER_PREFIX, "");
byte[] decoded = null;
try {
decoded = Base64.getDecoder().decode(authToken);
} catch (IllegalArgumentException ex) {
return false;
}
String decodedString = new String(decoded);
StringTokenizer tokenizer = new StringTokenizer(decodedString, ":");
String username = null, password = null;
if(tokenizer.countTokens() < 2)
return false;
username = tokenizer.nextToken();
password = tokenizer.nextToken();
if (DbController.isValid(username, password, rolesAnnotation.value()))
return true;
}
return false;
}
private Response createUnauthorizedResponse(String msg) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity("{ \"Unauthorized\" : \"" + msg + "\" }")
.type(MediaType.APPLICATION_JSON)
.build();
}
}
It works fine with postman. And I do realize that the main usage of such apis is in well other programs.
But it would be nice, if opened in a browser it would ask you to enter your credentials, instead of just telling you that you are not authorized, with no way to really enter your credentials. Unless you do some trickery to manually put it in the header, but then you might as well just use postman.
If I put a security constraint with auth-constraint role admin it does give a login dialog, but then the authorization does not work and it just keeps asking for authorization.
Is there anything else that I can do instead of containerRequestContext.abortWith? Or do I need to use a completely different approach and it just won´t work with ContainerRequestFilter?
You need to add the WWW-Authenticate header to the response that you abort with. This header tells the browser that it should present the default browser login form.
private static final String CHALLENGE_FORMAT = "%s realm=\"%s\"";
private Response createUnauthorizedResponse() {
return Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE, String.format(CHALLENGE_FORMAT, "Basic", "Access"))
.type(MediaType.TEXT_PLAIN_TYPE)
.entity("Credentials are required to access this resource.")
.build();
And here's what the login should look like on Chrome
Using Spring Boot I am configuring the following filter
#Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
Map<String, String> filterChainDefinitionMapping = new HashMap<>();
/*
* URL path expressions are evaluated against an incoming request in the order they are defined and the FIRST MATCH WINS. For example, let's asume that there are the following chain definitions:
/account/** = ssl, authc
/account/signup = anon
If an incoming request is intended to reach /account/signup/index.html (accessible by all 'anon'ymous users), it will never be handled!. The reason is that the /account/** pattern matched the incoming request first and 'short-circuited' all remaining definitions.
Always remember to define your filter chains based on a FIRST MATCH WINS policy!
* */
filterChainDefinitionMapping.put("/login.html", "authc");
filterChainDefinitionMapping.put("/logout", "logout");
filterChainDefinitionMapping.put("/css/**", "anon");
filterChainDefinitionMapping.put("/register/**", "anon");
filterChainDefinitionMapping.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMapping);
shiroFilter.setSecurityManager(securityManager());
shiroFilter.setLoginUrl("/login.html");
shiroFilter.setSuccessUrl("/");
shiroFilter.setUnauthorizedUrl("/unauthorized.html");
Map<String, Filter> filters = new HashMap<>();
filters.put("anon", new AnonymousFilter());
filters.put("authc", new FormAuthenticationFilter());
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/login.html?logout");
filters.put("logout", logoutFilter);
filters.put("roles", new RolesAuthorizationFilter());
filters.put("user", new UserFilter());
shiroFilter.setFilters(filters);
return shiroFilter;
}
However, whenever I try to login with wrong credentials the redirection never happens. I do get the "shiroLoginFailure" attribute holding the UnknownUserException.
(Logging in with the correct credentials works fine)
Any ideas?
Mariosk89, how do you resolve the /login.html?
It might be need to resolve redirect like this:
#RequestMapping("/login")
public String login(String username, String password) {
Subject currentUser = SecurityUtils.getSubject();
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
try {
currentUser.login(new UsernamePasswordToken(username, password));
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
return "login";
}
return "redirect:index";
} else {
return "login";
}
}
Reference: https://github.com/lenicliu/examples/tree/master/examples-spring-boot/examples-spring-boot-shiro
For more exception solution, refer http://shiro.apache.org/10-minute-tutorial.html
try {
currentUser.login( token );
//if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
//username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
//password didn't match, try again?
} catch ( LockedAccountException lae ) {
//account for that username is locked - can't login. Show them a message?
}
... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
//unexpected condition - error?
}
This is a follow-up question to the one I asked yesterday.
The log-in page is supposed to redirect to main page after correct username and password input, with server side returning an empty string (""). If either is incorrect, server side code returns "Username or Password are incorrect".
The page functionality worked well but when I was testing using my client side code using a correct pair of username and password, it returns "Username or Password are incorrect", with response returning 200OK.
Below is my client side code:
public static void main(String[] args){
ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target = client.target("http://localhost:8080
/qa-automation-console").path("authenticate");
Form form = new Form();
form.param("username", "username");
form.param("password", "password");
Response response = target.request().post(Entity.form(form));
//The response was 200OK.
System.out.println(response.readEntity(String.class));
}
Instead of other problems including HTML and Web.xml dependency, now I suspect the client code I wrote was not correct and when the request is sent it does not contain the correct username and password. The server side code is below:
#POST
#Produces("text/plain")
#Path("authenticate")
public String authenticate(#Context HttpServletRequest req, #QueryParam("username")
String username, #QueryParam("password") String password)
throws Exception {
Environments environments = new DefaultConfigurationBuilder().build();
final ALMProfile profile = new ALMProfile();
profile.setUrl(environments.getAutomation().getAlmProfile().getUrl());
profile.setUsername(username);
if ( !Strings.isNullOrEmpty(password) ) {
String encryptedPassword = EncryptionUtils.encrypt(password);
profile.setPassword(encryptedPassword);
}
try (ALMConnection connection = new ALMConnection(profile);) {
if (connection.getOtaConnector().connected()) {
req.getSession(true).setAttribute("username", username);
req.getSession(true).setAttribute("password", profile.getPassword());
return "";
}
} catch (Exception e) {
e.printStackTrace();
return "Username or Password are incorrect";
}
return "Username or Password are incorrect";
}
Can someone point out if the client code submits the correct request?
First of all, you probably need to check what's in your stack trace. And the value of username and password. I suspect them to be null.
Secondly, I think the problem is coming from the #QueryParam annotation.
You must use instead #FormParam
You use QueryParam when your url contain params as:
www.test.com/test?username=test
When you're sending data in a Form, you must use the annotation #FormParam
so here is my issue: I am working on a mobile App, that requires a login. I am programming in Android Studio / Java. Got a quite good experience in Java but i've never done networking.. There is a .asp script on a server that processes the login, which i need to send the login data to. The best way to solve this i think is a HTTP - Request, because if you enter the url of the script into the browser, followed by the query string containing the login data, you already get a response.
http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android
returns a Json String containing a profile token and a timestamp and the profile name.Like:
{"profil_token":"qn2hJcRQixYjG7yyW956g1407921902","profil_name":"Marc Klingenhäger","timestamp":"1407921902"}
This profile token is then attached to every url the user requests, and in that way the user gains permission to all the websites.
I read that you can do the same thing with a http GET request, but me and my coworker worked on this
(such a simple thing) for nine ours and didn't get our code working...
We tried out heaps of snippets, this is our most simple attempt:
In the Main activity, on clicking the button that leads to the login, LoginActivity.class is called using a Intent.
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
After entering the user data, the user clicks the login button, and the method attemptLogin(); gets called.
public void attemptLogin() {
if (mAuthTask != null) {
return;
}
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
// Check for a valid password, if the user entered one.
if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
mPasswordView.setError(getString(R.string.error_invalid_password));
focusView = mPasswordView;
cancel = true;
}
// Check for a valid email address.
if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
cancel = true;
} else if (!isEmailValid(email)) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
cancel = true;
}
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mAuthTask = new UserLoginTask(email, password);
mAuthTask.execute((Void) null);
}
}
So after some error detection, the class userLoginTask (subclass of AsyncTask) is initialized to handle the networking stuff, because initializing the http request in the main thread seems to cause an exception. We didn't manage to code a HTTP - Request here so far.. (thats the main problem)
public class UserLoginTask extends AsyncTask {
private final String mEmail;
private final String mPassword;
UserLoginTask(String email, String password) {
mEmail = email;
mPassword = password;
}
#Override
protected Boolean doInBackground(Void... params) {
// TODO: attempt authentication against a network service.
try {
// Simulate network access.
Thread.sleep(2000);
} catch (InterruptedException e) {
return false;
}
for (String credential : DUMMY_CREDENTIALS) {
String[] pieces = credential.split(":");
if (pieces[0].equals(mEmail)) {
// Account exists, return true if the password matches.
return pieces[1].equals(mPassword);
}
}
// TODO: register the new account here.
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
showProgress(false);
if (success) {
finish();
} else {
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
So my question is basically, how can i initialize a HTTP - Request in the UserLoginTask class. Any Ideas? Thanks in advance! :)
Falco
The easiest way is to use a URL object and open a stream to your HTTP server.
The server response can be read through this stream:
String url = "http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android";
try {
URL u = new URL(url);
InputStream is = u.openStream(); // Opens streaming connection to url
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuffer result = new StringBuffer(); // Buffer to store saved data
String input = null;
while((input = reader.readLine()) != null) {
// Read data until the end of the stream
result.append(input);
}
// Do something with result here
} catch (IOException e) {
e.printStackTrace();
}
When you have retrieved the data as a string, you can parse the JSON to get the profile_token
Use Android's Volley http://developer.android.com/training/volley/index.html and issue an HTTP POST request, sending username / password.
I advise hashing the password (MD5 or something else - depends on what the back-end handles to decrypt).
Google suggests using HttpUrlConnection.
An example that should do what you want is very simple, especially when using GET. First, construct an URL from String. Your response is InputStream, which you parse to JSONObject and obtain your token.
URL url = new URL("http://sampleurl.info/actions/checklogin.asp?userName=klingenhaeger&password=droid&device=android");
//later:
URL url = new URL("http://sampleurl.info/actions/checklogin.asp?token=abcde");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
finally {
urlConnection.disconnect();
}
}
This is recommended approach, since it does not require any external libraries, and it can easily be converted to use POST instead of GET and HTTPS instead of HTTP.
I currently have a working web app, but I need to provide means for friend website to consume my data.
There is currently JSON response in place which retrieves some data from my website to caller. It's without authentication currently and I'd like to implement some kind of per request authentication.
My web app has users which are logged in and there is a authentication in place for that. But
I have 3 requests in total for which callers can get data off of my website, what would be the simplest way to add some kind of authentication just for those 3 requests?
I'm using play framework + java
Imo the best options for this would be in the order of simplicity:
Basic authentication (since it's possible to choose either to auth once and then do session-base user recognition or authorize on every request)
2-way SSL
Combination of both
What toolkit do you use for authentication part?
I personally stuck with play-authenticate. So I might be able to answer you question in regard to this toolkit, please apply it to your particular toolkit as needed.
I will provide Basic authentication example as the easiest one. The benefit is: you could start with it and add on top it later (e.g. add Client certificate authentication via Apache later on).
So, my controller code snippet
#Restrict(value = #Group({"ROLE_WEB_SERVICE1"}), handler = BasicAuthHandler.class)
public static Result ws1() {
return TODO;
}
And the authentification handler itself
public class BasicAuthHandler extends AbstractDeadboltHandler {
public static final String HEADER_PREFIX = "Basic ";
private static final String AUTHORIZATION = "authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
#Override
public Result beforeAuthCheck(final Http.Context context) {
return basicAuthenticate(context);
}
private Result basicAuthenticate(Http.Context context) {
if (PlayAuthenticate.isLoggedIn(context.session())) {
// user is logged in
return null;
}
final String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null || !authHeader.toLowerCase().startsWith(HEADER_PREFIX.toLowerCase())) {
return onAuthFailure(context, "Basic authentication header is missing");
}
final String auth = authHeader.substring(HEADER_PREFIX.length());
final byte[] decodedAuth;
final String[] credentials;
try {
decodedAuth = Base64.base64ToByteArray(auth);
credentials = new String(decodedAuth, "UTF-8").split(":");
} catch (final IOException e) {
Logger.error("basicAuthenticate", e);
return Results.internalServerError();
}
if (credentials.length != 2) {
return onAuthFailure(context, "Could not authenticate with absent password");
}
final String username = credentials[0];
final String password = credentials[1];
final AuthUser authUser = new AuthUser(password, username);
final Enum result = AuthProvider.getProvider().loginUser(authUser);
if ("USER_LOGGED_IN".equals(result.name())) {
PlayAuthenticate.storeUser(context.session(), authUser);
return null;
}
return onAuthFailure(context, "Authenticate failure");
}
#Override
public Subject getSubject(final Http.Context context) {
// your implementation
}
#Override
public Result onAuthFailure(final Http.Context context,
final String content) {
// your error hangling logic
return super.onAuthFailure(context, content);
}
}
Hopefully it fills in some blanks