The question is very simple. I'd like to restrict user access with same login from different machines/browsers: only one live user session is possible.
Apache shiro library is used for user authentification and managment.
Of course this could be done using simple synchornized maps and etc. But the question is: Has Apache Shiro special mechanisms for that or not?
Another variant of this question: how to reveice the list of all subjects who are logged in the system using apache shiro?
UPD:
To clarify my question. My desire is to have some code like this (I known, that there isn't such class exception, but the idea must be more clean):
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(login, password);
try {
currentUser.login(token);
} catch (AlreadyAuthenticatedException aae) {
errorMsg = "You should logoff on another machine!";
}
The Shiro sessions are stored in SessionDAO with sessionId as keys. Without extra effort you cannot access a session by a principal (user name). However, you could extend DefaultSecurityManager and check all active sessions by SessionDAO.getActiveSessions.
The following codes could be a simple example (suppose you are not using WebSubject):
public class UniquePrincipalSecurityManager extends org.apache.shiro.mgt.DefaultSecurityManager {
#Override
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
String loginPrincipal = (String) token.getPrincipal();
DefaultSessionManager sm = (DefaultSessionManager) getSessionManager();
for (Session session : sm.getSessionDAO().getActiveSessions()) {
SimplePrincipalCollection p = (SimplePrincipalCollection) session
.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (p != null && loginPrincipal.equals(p.getPrimaryPrincipal())) {
throw new AlreadyAuthenticatedException();
}
}
return super.login(subject, token);
}
}
Related
I've got the following set up:
Central auth server written with spring boot that is currently working (I can curl and receive an access token, jdbc token store, etc)
Multiple applications owned by the same developer, sharing the same customer base on different domains. IE: John Doe for app1 is the same as John Doe for app2.
I have an existing application (app1 above) that is jsf 2.2 with spring security configured for login purposes. That application works stand alone right now, with it's own user base.
This is the flow I am trying to obtain:
Resource Owner Password Credential OAuth Flow
So we would want:
User goes to app1
User enters user and password into app1 login page
User hits "login"
Some sort of configuration in Spring would then take the loginByUsername request, get access token from the central oauth server
We now have app1 access - the user could have one of three roles (ADMIN, USER, SUPERUSER).
When they go to (say) app1/views/createEntry.xhtml, we would confirm the access token we currently have is still active on the auth server.
The resource server would technically be the resources on the app1 server (right?)
I'm new to this oauth2.0 process (and spring really), but I think this is the flow I want. How do I set this up with Spring Security? I've seen a security setting called oauth2login() that I think is what we COULD want, but I think that is more authorization code flow.
I haven't found a very good example of this using the password flow.
I do trust each of the applications in this process, hence the password flow. We control the network that maintains traffic between the auth server and the other applications.
Edit: SSO isn't an option because of requirements and our customer base. The applications are unique enough that it doesn't make sense, but the user should be able to log into any of our applications with those credentials.
Edit 2: Sorry for second edit. I would like to add that I've added a resource configuration on app1 and it actually seems like it works - I've secured anything /views/* and when I attempt to go their, I get the expected "Full Authentication required" message.
Edit 3: I think I am making some progress -
First, I created a spring component that implements AuthenticationProvider and then overwrote the authenticate method so that I created a ResourceOwnerPasswordResourceDetails object with all my properties (client id, client secret, grant type, scope, etc) and called the authorization server to get a token. My excitement to see my log refresh for the authorization server was high.
Next step I need to figure out is how to generate an extension of org.springframework.security.core.userdetails.User so that I can store the privileges for the user.
Also - I can't quite figure out yet how the token is stored. I know the auth server generates a token and stores in jdbc, but where/how does the token get stored on the client side?
For those that were curious, here is how I set up the authentication provider on my client (app1). I still have issues with the resource server (ill ask a separate question), but here is what I did:
Custom authenticator:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private AppUserDAO appUserDAO;
private String accessTokenUri = "http://localhost:8080/oauth/token";
private String clientId = "clientid";
private String clientSecret = "clientsecret";
public AccessTokenProvider userAccessTokenProvider() {
ResourceOwnerPasswordAccessTokenProvider accessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
return accessTokenProvider;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String username = authentication.getName();
final String password = authentication.getCredentials().toString();
List<String> scopes = new ArrayList<String>();
scopes.add("read");
final ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setUsername(username);
resource.setPassword(password);
resource.setAccessTokenUri(accessTokenUri);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
resource.setGrantType("password");
resource.setScope(scopes);
// Generate an access token
final OAuth2RestTemplate template = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
template.setAccessTokenProvider(userAccessTokenProvider());
OAuth2AccessToken accessToken = null;
try {
accessToken = template.getAccessToken();
System.out.println("Grabbed access token from " + accessTokenUri);
}
catch (OAuth2AccessDeniedException e) {
if (e.getCause() instanceof ResourceAccessException) {
final String errorMessage = String.format(
"While authenticating user '%s': " + "Unable to access accessTokenUri '%s'.", username,
accessTokenUri);
throw new AuthenticationServiceException(errorMessage, e);
}
throw new BadCredentialsException(String.format("Access denied for user '%s'.", username), e);
}
catch (OAuth2Exception e) {
throw new AuthenticationServiceException(
String.format("Unable to perform OAuth authentication for user '%s'.", username), e);
}
// Determine roles for user
List<GrantedAuthority> grantList = ...
// Create custom user for the principal
User user = .....
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, null /*dont store password*/, grantList);
return token;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Security configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
....
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}
I'm trying to solve a puzzle with enabling OAuth2-based authentication for my Feign client that is used for cross-service communication.
In normal cases, when a user pushes a request through API, I'm able to take all authentication details needed from the Spring's SecurityContextHolder (as it normally does its job and fills all the details objects) and enhance Feign's Request as follows:
public class FeignAccessTokenRelyInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(FeignAccessTokenRelyInterceptor.class);
#Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
}
However, when it comes to scheduled calls that are triggered inside the system, the SecurityContext is obviously empty. I'm filling it with UserInfoTokenServices by manually requesting the access token by client credentials flow beforehand and loading user details:
OAuth2Authentication authentication = userInfoTokenServices.loadAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
But such construction doesn't fill OAuth2Authentication.details, on which I rely to get an access token. I tried extending OAuth2AuthenticationDetails, but the only constructor requires HttpServletRequest which is hard to get inside a scheduled task, and making a dummy instance of it feels like a bad choice.
So far, I see the only adequate option to make a separate custom implementation of details holder and pass it to OAuth2Authentication along with the access token I have. And then pick it up in FeignAccessTokenRelyInterceptor.
The question
Maybe there are some other options where I can store my access token in the security context and reliably get it from there, in order not to produce new custom classes?
Will be glad for any help.
Some related links I've studied:
How to get custom user info from OAuth2 authorization server /user endpoint
Spring Boot / Spring Cloud / Spring Security: How to correctly obtain an OAuth2 Access Token in a scheduled task
Spring #FeignClient , OAuth2 and #Scheduled not working
How can I authenticate a system user for scheduled processes in Spring?
For the history, hope that'd help someone like me struggling with that.
I didn't find a better way than the initial one and made a custom InternalOAuth2Details to hold a token value obtained from Spring's OAuth services. Then, in the FeignAccessTokenRelyInterceptor I simply check if current details are InternalOAuth2Details and try to get a token value from it, as follows:
#Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
} else if (auth.getDetails() instanceof InternalOAuth2Details) {
InternalOAuth2Details details = (InternalOAuth2Details) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
I bet it isn't the best solution, but it seems to work quite stable as of now.
I am trying to get Auth0 integrated into my web app which uses the spark-java framework.
The problem is while the authentication works perfectly, including the callback(I see the new user created on Auth0's website and my website gets redirected), I can't access the logged in user info. I've tried several methods like SessionUtils.getAuth0User(request.raw()) and none of them are working.
For example in the provided tutorial here: https://github.com/auth0-samples/auth0-servlet-sample/tree/master/01-Login
they access the logged in user info like so:
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
final Auth0User user = SessionUtils.getAuth0User(req);
if (user != null) {
req.setAttribute("user", user);
}
req.getRequestDispatcher("/WEB-INF/jsp/home.jsp").forward(req, res);
}
I've tried doing something similar with Spark but since the get works a bit differently in Spark I do this:
port(Integer.valueOf(System.getenv("PORT")));
staticFileLocation("/spark/template/freemarker");
String clientId = System.getenv("AUTH0_CLIENT_ID");
String clientDomain = System.getenv("AUTH0_DOMAIN");
get("/", (request, response) ->
{
Map<String, Object> attributes = new HashMap<>();
Auth0User user = SessionUtils.getAuth0User(request.raw());
if(user != null) {
attributes.put("user", user);
attributes.put("loggedIn" , true);
}
else
attributes.put("loggedIn" , false);
attributes.put("clientId" , clientId);
attributes.put("clientDomain" , clientDomain);
return new ModelAndView(attributes, "index.ftl");
}, new FreeMarkerEngine());
The code is always reporting the user as null even though the user is created and stored in the database and the signin works properly with no runtime or console errors. The other methods I tried I replaced the line where I set the user variable and wrote the following.
Alternate Method 1:
Auth0User user = (Auth0User) request.session().attribute("auth0User");
Here auth0User is the same string literal Auth0 uses in their implementation of SessionUtils as shown in their source code referenced here: https://github.com/auth0/auth0-java-mvc-common/blob/master/src/main/java/com/auth0/SessionUtils.java
Alternate Method 2:
Auth0User user = (Auth0User) request.raw().getUserPrincipal();
In addition this is my javascript code running client side for the authentication:
var lock = new Auth0Lock('${clientId}', '${clientDomain}', {
auth: {
redirectUrl: 'http://localhost:5000/build',
responseType: 'code',
params: {
scope: 'openid user_id name nickname email picture'
}
}
});
$(document).ready(function()
{
$('.signup').click(function()
{
doSignup();
});
});
function doSignup() {
lock.show();
}
I have no idea why user is being evaluated to null every time and I would love some feedback on what I'm doing wrong. Thanks.
In order for you to get a non null user instance from SessionUtils.getAuth0User(req) some piece of code must first call SessionUtils.setAuth0User. This should be done when you receive confirmation that the user authenticated with success.
In the auth0-servlet-sample you were using as reference this is done by configuring an Auth0ServletCallback that will handle requests performed to /callback endpoint. Since the Auth0ServletCallback calls (see code below) the set user for you, in the servlet example you can then get the user with success.
protected void store(final Tokens tokens, final Auth0User user, final HttpServletRequest req)
{
SessionUtils.setTokens(req, tokens);
SessionUtils.setAuth0User(req, user);
}
At the moment the available samples (auth0-servlet-sample, auth0-servlet-sso-sample, auth0-spring-mvc-sample, auth0-spring-security-api-sample and auth0-spring-security-mvc-sample) don't include one for spark-java so I can't refer you to any sample.
In order to solve this you have to include additional logic to process the result of the authentication operation in your spark-java application and in case of success call the SessionUtils.setAuth0User yourself if you then want to use the corresponding SessionUtils.getAuth0User method.
For general guidance on integrating a web application with Auth0 check Integrating a Web App with Auth0.
I'm using this ContainerRequestFilter to check HTTP Basic credentials.
private class Filter implements ResourceFilter, ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
String auth = request.getHeaderValue("Authorization");
if (auth == null || !auth.startsWith("Basic ")) {
throw new NotAuthorizedException("FAILED\n");
}
auth = Base64.base64Decode(auth.substring("Basic ".length()));
String[] vals = auth.split(":");
String username = vals[0];
String password = vals[1];
boolean validUser = database.Users.validate(username, password);
if (!validUser) {
throw new NotAuthorizedException("FAILED\n");
}
return request;
}
...
}
So by the time I get to this point, I've authenticated the user. Now how I can get the username?
#GET
#Path("some_kind_of_report_or_something")
#Produces(MediaType.TEXT_PLAIN)
public String fetchAReportOrSomething() {
// At this point, I know that the user has provided good credentials,
// now I need get the user's username as a String
String username = ???;
}
I suppose I could use HttpContext.getRequest() and do the same thing as in the AuthFilter (I'd move that username/password extraction logic to its own method). In the filter, can I somehow store the extracted username somewhere in the request object so it gets passed on to this handler?
(By the way, is there a better way to extract the username and password than what I've done in the filter? If so, let me know in a comment.)
This blog entry should enlighten you:
http://plaincode.blogspot.pt/2011/07/openid-authentication-example-in-jersey.html
Take a look how it's done in a working application: www.s3auth.com. The source code is available at github. As you can see on the site, facebook and google authentication mechanisms are used. The application is using JAX-RS/Jersey.
Can someone explain me how to write a custom login interceptor that checks username, password and also checks if the users validity date is greater than the current date. Im new to java programming & struts 2...i would really appreciate step by step info. I get the username, etc info by manual jdbc connection...i have a jndi setup for that. This also needs to have session management.
So a step by step with the following code samples would be nice,
1) The dao using jndi to get username,etc from DB
2) The login action with session aware
3) interceptor
4) login.jsp
5) struts.xml definition for the interceptor
6) task.jsp and task2.jsp ( internal pages that can only be seen if user is logged in)
Thank you!
You are on the right track.
There are many articles on that topic (google it). Choose one and try to understand it. The interceptor part should look something like this:
public String intercept (ActionInvocation invocation) throws Exception {
// Get the action context from the invocation so we can access the
// HttpServletRequest and HttpSession objects.
final ActionContext context = invocation.getInvocationContext ();
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
HttpSession session = request.getSession (true);
// Is there a "user" object stored in the user's HttpSession?
Object user = session.getAttribute (USER_HANDLE);
if (user == null) {
// The user has not logged in yet.
// Is the user attempting to log in right now?
String loginAttempt = request.getParameter (LOGIN_ATTEMPT);
if (! StringUtils.isBlank (loginAttempt) ) { // The user is attempting to log in.
// Process the user's login attempt.
if (processLoginAttempt (request, session) ) {
// The login succeeded send them the login-success page.
return "login-success";
} else {
// The login failed. Set an error if we can on the action.
Object action = invocation.getAction ();
if (action instanceof ValidationAware) {
((ValidationAware) action).addActionError ("Username or password incorrect.");
}
}
}
// Either the login attempt failed or the user hasn't tried to login yet,
// and we need to send the login form.
return "login";
} else {
return invocation.invoke ();
}
}
Above code sample is part of this article where you will also find other steps.
Another way I would recommend is integration of spring security with Struts 2. That way you get secured and proven configurable security stack.