I'm new to spring-security. I understood basics of spring-security about authentication-manager and other stuff.
I'm using angularjs as front end, and spring 3.0.5 as backend.
I want to add spring security for role based authorization to my existing project.
I also want to authenticate user using office 365. So, i created application in https://apps.dev.microsoft.com/ and given redirect URI as my localhost action.
Using my code, I'm able to authenticate using office 365 and able to get username in redirected action.
#RequestMapping(value="/getCodeAndProcess", method=RequestMethod.POST)
private ModelAndView getCodeAndProcess(#RequestParam("code") String code, HttpServletRequest request) throws Exception {
HttpSession session = null;
try {
session = request.getSession(false);
session.setMaxInactiveInterval(45*60);
String username = helper.processCodeAndGetUsername(code); //this is I'm getting using office 365.
String userRole = helper.getUserRoleBasedOnUsername(username);
System.out.println("================userRole================="+userRole);
if(username != "") {
session.setAttribute("username", username);
session.setAttribute("userRole", userRole);
}else {
session.invalidate();
throw new Exception("username not found");
}
return new ModelAndView("redirect:/adminDashboard/showDashboard");
}catch(Exception e) {
log.error("Error while processing on users authentication.",e );
session.invalidate();
return new ModelAndView("redirect:/login/loginPage");
}
}
Now, I don't get how to configure username and roles in security-context.xml, so that i can use #PreAuthorize("hasRole('ROLE_USER')") , isAuthenticated(), <sec:authorize in my application. (What to add in security-context.xml? So, that it bind username with role as it doing in form login scenario.)
Can you please help to understand this workflow?
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 try to get user's name like this:
public static String getUsername() {
SecurityContext context = SecurityContextHolder.getContext();
UserDetails userDetails = (UserDetails) context.getAuthentication().getPrincipal();
return userDetails.getUsername();
}
It works fine when just loggined, but when go to a different page context becomes org.springframework.security.core.context.SecurityContextImpl#ffffffff: Null authentication with authentification = null.
How do I retrieve logged in user with Spring Security in Spring Boot correctly?
Spring Boot version is 2.0.3.RELEASE.
UPD:
To clarify: After navigating to another page SecurityContextHolder.getContext() gives a context with authentification = null. So context.getAuthentication().getPrincipal() throws NullPointerException because context.getAuthentication() is null.
Don't know what is wrong, but I found a workaround - I save username in session.
Since I use Vaadin - I use VaadinSession, but I believe HttpSession can be used as well.
public static String getUsername() {
VaadinSession session = VaadinSession.getCurrent();
if(session.getAttribute("username") == null) {
SecurityContext context = SecurityContextHolder.getContext();
UserDetails userDetails = (UserDetails) context.getAuthentication().getPrincipal();
session.setAttribute("username", userDetails.getUsername());
}
return (String) session.getAttribute("username");
}
The only thing - this code works only in Components, so you must make sure to use it from autowired classes.
I am doing a project on library management system in spring boot security.
In order to calculate the fines for the issued books according to the roles i wan the current user role after borrowing a book.
Current user name, role book_id and fine will be stored in other table.
I am able to get the current users username, but not able to get role the current user.
Could someone please help me out!
//Part of Controller class
#RequestMapping("/homepage/borrowBook")
public String addBookings(Bookings bk, HttpServletRequest rqst) {
rqst.setAttribute("mode", "MODE_BORROW");
return "homepage";
}
#PostMapping("/homepage/save-borrow")
public String saveBorrow(Bookings bk, HttpServletRequest rqst, Authentication auth) {
rqst.setAttribute("mode", "MODE_BORROW");
if (BookRepo.exists(bk.getBook_id())) {
bk.setUser(auth.getName());
/////here i want the current user authority to be saved/checked.
bookingsRepo.save(bk);
return "homepage";
} else {
rqst.setAttribute("error", "Book doesn't exist");
return "homepage";
}
}
You can use Authentication.getAuthorities() to get the roles of the currently logged in user.
You can get the authorities using the SecurityContextHolder or through the inject Authentication object at your controller.
Find below through the SecurityContextHolder
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Collection<SimpleGrantedAuthority> list = (Collection<SimpleGrantedAuthority>) auth.getAuthorities();
for (SimpleGrantedAuthority permission : list) {
System.out.println(permission.getAuthority());
}
If you need any other information about the logged in user, you can access the UserDetails as follows
User userDetails = (User) auth.getPrincipal();
I am creating a Spring MVC web application using spring security for authentication.
My application has a requirement that the user is logged in automatically when the user accesses a particular URL, say http://www.example.com/login?username=xyz
The user xyz is in the database, and I need to create a session for this user automatically.
Request you to let me know how this can be achieved.
Thanks!
You can do something like this, the idea would be to retrieve the user from the database, make the checks you want and then use the UsernamePasswordAuthenticationToken to create a new Principal into the current session.
The use of the returned Principal of the method is for subsequent uses on the same request, as the Principal won't be available to be retrieved by normal means until the next request.
public Principal doAutoLogin(String username, String password, HttpServletRequest hreq, HttpServletResponse hres, boolean rememberMe) {
User user = getUserFromDatabase(username);
if(user != null && passwordsMatch(password, user.getPassword())) {
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(hreq));
if(LOG.isDebugEnabled())
LOG.debug("Logging in with "+ token.getPrincipal());
SecurityContextHolder.getContext().setAuthentication(token);
if(rememberMe) {
rememberMeServices.onLoginSuccess(hreq, hres, token);
}
return token;
} catch (Exception e) {
SecurityContextHolder.getContext().setAuthentication(null);
LOG.error("Failure in autoLogin", e);
return null;
}
}
else {
throw new IllegalArgumentException("The specified credentials can't be found");
}
}
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);
}
}