How to set user role in Spring Security 3? - spring

I am working on JAVA EE technologies to learn JSF-Spring-Hibernate vs...
I am trying to do web application with diffirent user roles. Now, I have only one user role ROLE_USER. I could not change that role. I tried debug mod to understand how we set this role but I could not understand where it is happening.
So, my main problem is I can not create any other ROLE in my application. If I do not use <secured attributes="ROLE_USER" /> in my flow (I am using spring web-flow) everyone can reach that page. If I do only ROLE_USER. But I want to create new role named ROLE_ADMIN and only let ROLE_ADMIN to reach that page.
NOTE: I did not add all files here cause is spoken to me that only add related classes and files. If you need extra information please let me know.
This is the tutorial soruce code that I am following.
https://code.google.com/p/jee-tutorial-youtube/source/browse/
security-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<security:http auto-config="true">
<security:form-login login-page="/app/main"
default-target-url="/app/account" />
<security:logout logout-url="/app/logout"
logout-success-url="/app/main" />
</security:http>
<security:authentication-manager>
<security:authentication-provider
user-service-ref="userServiceImp">
<security:password-encoder hash="md5" />
</security:authentication-provider>
</security:authentication-manager>
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userServiceImp" />
<property name="hideUserNotFoundExceptions" value="false" />
</bean>
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<ref bean="daoAuthenticationProvider" />
</constructor-arg>
</bean>
This is my UserAuthenticationProviderServiceImp class where I authenticatethe the user.
public boolean processUserAuthentication(Users user) {
try {
Authentication request = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
return true;
} catch(AuthenticationException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), "Sorry!"));
return false;
}
}
And I figure out that this function, im my Service class, is called in processUserAuthentication.Then I thought this is the function where Roles are setted.
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
Users user = userDao.loadUserByUserName(userName);
if (user == null) {
throw new UsernameNotFoundException(String.format(
getMessageBundle().getString("badCredentials"), userName));
}
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("USER_ROLE"));
User userDetails = new User(user.getUserName(), user.getPassword(),
authorities);
return userDetails;
}
Update:
In my project I have to use this syntax to create roles: ROLE_X where x is any string. Example; we can have ROLE_MYNAME but we can not have juts MYNAME or MYNAME_ROLE as role. It has to start with ROLE_. I am still trying to find what couses this problem. I will update If I found any answer.
EDIT:
There is an detailed explanation why we has to use ROLE_ in this page;
http://bluefoot.info/howtos/spring-security-adding-a-custom-role-prefix/

Right now USER_ROLE is hardcoded in your application. Ideally User to Role mapping would come from Authorities table in database. This mapping info then can be retrieved using a DB query and then added to the authorities collection.
authorities.add(loadUserAuthorities(user.getUsername()));
protected List<GrantedAuthority> loadUserAuthorities(String username) {
List<GrantedAuthority> authorities = null;
Object[] params = { username };
authorities = authoritiesByUsernameMapping.execute(params); // Query to get Authorities
return authorities;
}
Spring Security DB schema

Related

Does OAuth2 allow for authorization using non-password or custom credentials?

I'm using Spring Security OAuth2. The client application (that we own) makes a "password" grant request that passes the user's username and password. Just like the draft specifies.
I need this mechanism to also support other types of credentials, like card number, PIN, and even a pre-authenticated, password not required grant.
Please keep in mind, these requests will only be permitted by a privileged client_id, one that will only be used from the application we own.
Dave, thanks for the quick response. I actually found the perfect solution, one which you took part in. It has to do with "custom-grant" token granters... https://jira.spring.io/browse/SECOAUTH-347
Had I updated my rather old 1.0.0.M5 version I might have known about those.
My approach was to extend AbstractTokenGranter with a class that supports a custom grant type (I call it "studentCard"). Once an authentication request makes it here, I examine the parameter list just like ResourceOwnerPasswordTokenGranter, but instead look for my custom "cardNumber" parameter. I then pass my own, id-based version of UsernamePasswordAuthenticationToken to my AuthenticationProvider, which knows how to authenticate users based on id card.
Here is the custom token granter class I came up with:
public class StudentCardTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "studentCard";
private final AuthenticationManager authenticationManager;
public StudentCardTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService) {
super(tokenServices, clientDetailsService, GRANT_TYPE);
this.authenticationManager = authenticationManager;
}
#Override
protected OAuth2Authentication getOAuth2Authentication(AuthorizationRequest clientToken) {
Map<String, String> parameters = clientToken.getAuthorizationParameters();
String cardNumber = parameters.get("cardNumber");
Authentication userAuth = new StudentCardAuthenticationToken(cardNumber);
try {
userAuth = authenticationManager.authenticate(userAuth);
} catch (BadCredentialsException e) {
// If the username/password are wrong the spec says we should send 400/bad grant
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate student: " + cardNumber);
}
return new OAuth2Authentication(clientToken, userAuth);
}
}
And my authorization server config:
<!-- Issues tokens for both client and client/user authorization requests -->
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password authentication-manager-ref="myUserManager" />
<oauth:custom-grant token-granter-ref="studentCardGranter" />
</oauth:authorization-server>
<bean id="studentCardGranter" class="com.api.security.StudentCardTokenGranter">
<constructor-arg name="authenticationManager" ref="myUserManager" />
<constructor-arg name="tokenServices" ref="tokenServices" />
<constructor-arg name="clientDetailsService" ref="clientDetails" />
</bean>
The spec doesn't explicitly allow direct, non-password-based exchange of user tokens between a client and the auth server directly. I think it would be quite natural to extend the password grant to other forms of authentication though. It's in the spirit of the spec, if not by the letter, so if you own both sides of the relationship there isn't much to go wrong. Spring OAuth doesn't explictly support anything that extends the password grant in this way, but its not hard to do (it's really just about the security of the /token endpoint). An alternative approach I've seen is to stick to the password grant protocol, but make the "password" a one-time token that the client can only get by knowing the user has authenticated in one of those alternative ways.

Getting logged in users with sessionRegistry not work when manually authenticate

I use spring security3 and spring mvc3 to build an web project. There is page called index.jsp, login user name and online user count will be displayed on
the top of this screen. There are 2 ways to login the system:
from login page, use default configuration post by 'j_spring_security_check'
ajax login with manually authentication
When I use login page to login into index page, both of count of online information and user name show correctly.
But when I use ajax login (manually authenticate), problem occurs: count of online user don't updated, it always displaying 0 while user name can show properly.
Part of the controller:
#Autowired
#Qualifier("authenticationManager")
AuthenticationManager authenticationManager;
#Autowired
SecurityContextRepository repository;
#RequestMapping(value="/ajaxLogin")
#ResponseBody
public String performLogin(
#RequestParam("j_username") String username,
#RequestParam("j_password") String password,
HttpServletRequest request, HttpServletResponse response) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
try {
Authentication auth = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
repository.saveContext(SecurityContextHolder.getContext(), request, response);
logger.info("Authentication successfully! ");
return "{\"status\": true}";
} catch (BadCredentialsException ex) {
return "{\"status\": false, \"error\": \"Bad Credentials\"}";
}
}
spring-security.xml
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/index" access="permitAll" />
<form-login login-page="/login" default-target-url="/index"
authentication-failure-url="/loginfailed" />
<logout logout-success-url="/logout" />
<session-management invalid-session-url="/index">
<concurrency-control max-sessions="1"
error-if-maximum-exceeded="false" />
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="
select login_id,login_pwd, is_enabled
from t_user where login_id=?"
authorities-by-username-query="
select u.login_id, r.authority from t_user u, t_roles r
where u.u_id = r.u_id and u.login_id =? " />
</authentication-provider>
</authentication-manager>
Method I used to get online login user count:
public class BaseController {
protected Logger logger = Logger.getLogger(this.getClass());
#Autowired
SessionRegistry sessionRegistry;
#ModelAttribute("numUsers")
public int getNumberOfUsers() {
logger.info("in getNumberOfUsers() ...");
return sessionRegistry.getAllPrincipals().size();
}
}
Code used to show login user name:
<div>
<security:authorize ifAllGranted="ROLE_USER">
<p>Welcome <security:authentication property="principal.username" />!
Logout</p>
</security:authorize>
</div>
code used to show count of logged in users:
<div style="color:#3CC457">
${numUsers} user(s) are logged in!
</div>
I guess that because when I manually authenticate, spring security not create new session for the user. I validate it by write customized SessionCounterListener.
public class SessionCounterListener implements HttpSessionListener {
private Logger logger = Logger.getLogger(this.getClass());
private static int totalActiveSessions;
public static int getTotalActiveSession(){
return totalActiveSessions;
}
#Override
public void sessionCreated(HttpSessionEvent event) {
totalActiveSessions++;
logger.info("sessionCreated - add one session into counter" + event.getSession().getId());
}
#Override
public void sessionDestroyed(HttpSessionEvent event) {
totalActiveSessions--;
logger.info("sessionDestroyed - deduct one session from counter" + event.getSession().getId());
}
}
Below is key content of log file for the action sequence: normal login -> normal logout -> ajax login -> ajax logout.
sessionDestroyed - deduct one session 1spueddcmdao019udc43k3uumw
sessionCreated - add one session 14nro6bzyjy0x1jtvnqjx31v1
sessionDestroyed - deduct one session 14nro6bzyjy0x1jtvnqjx31v1
sessionCreated - add one session e6jqz5qy6412118iph66xvaa1
Actually, ajax login/logout not give any output.
So now, how can I get correct login user count? And why the different authenticate ways has different method to deal with session? Any help will be appreciated.
As you are manually adding Principal to SecurityContext, it will not add user to SessionRegistry. You need to add user session to SessionRegistry manually.
SecurityContextHolder.getContext().setAuthentication(auth);
sessionRegistry.registerNewSession(request.getSession().getId(), auth.getPrincipal());
Hope it helps!!
In your Spring spring-security.xml file, the URL for the AJAX authentication (/ajaxLogin) is not explicitly allowed. Thus the request should be blocked by Spring. I would suggest to add this:
<intercept-url pattern="/ajaxLogin" access="permitAll" />

How to obtain a referrer URL with Spring MVC 3.1

I am building a website that has 2 pages. A recipe list page, a recipe detail page, and a sign in page. A user can sign in to the website by clicking on a sign in button on the recipe list page or recipe detail page. When a user click the sign in button, the user will be brought to the sign in page. I would like to redirect the user back again to the recipe detail page if they click the sign in button from the recipe detail page, or to the recipe list page if they click the sign in button from the recipe list page.
I wrote this method in a Controller class. This method will be called whenever user sign in to the website. I stored the referer URL into the session. The intention of saving this URL into a session is to keep track the page where user click on the sign in button. And also to redirect user to that page in the authentication handler that I wrote.
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String openLoginPage(Model uiModel, HttpServletRequest request) {
String referrer = request.getHeader("Referer");
request.getSession().setAttribute("url_prior_login", referrer);
return RECIPE_LOGIN_PAGE;
}
I also created an authentication handler class called SuccessHandler.
public class SuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
/*
* (non-Javadoc)
*
* #see org.springframework.security.web.authentication.
* SavedRequestAwareAuthenticationSuccessHandler
* #onAuthenticationSuccess(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse,
* org.springframework.security.core.Authentication)
*/
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
String url = (String) request.getSession().getAttribute("url_prior_login");
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal != null && principal instanceof RecipeUser) {
request.getSession().setAttribute("RecipeUser", (RecipeUser) principal);
}
getRedirectStrategy().sendRedirect(request, response, url);
}
}
This class will redirect user to either the recipe list page or the recipe detail page when they sign in to the website. I register this class inside a security-context.xml file:
<http use-expressions="true" auto-config="false" entry-point-ref="authenticationEntryPoint">
<intercept-url pattern="/login" access="permitAll" />
<form-login login-page="/login" authentication-failure-url="/loginfail"
default-target-url="/login"
authentication-success-handler-ref="successHandler" />
<logout logout-success-url="/" />
</http>
<authentication-manager alias="authManager">
<authentication-provider user-service-ref='myUserDetailsService' />
</authentication-manager>
<beans:bean id="myUserDetailsService" class="com.safe.stack.service.security.UserService">
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<beans:bean id="successHandler"
class="com.safe.stack.service.security.SuccessHandler" />
<beans:bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login"/>
</beans:bean>
Is this a good way to do what I want to achieve ? Is there a better way to do this ? I could not find any example on how to this using Spring MVC.
Thank you
This is how I do it in Spring 3.1.4
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
// Supplies the default target Url that will be used if
// no saved request is found in the session
setDefaultTargetUrl("/member/dashboard");
super.onAuthenticationSuccess(request, response, authentication);
}
Extending SavedRequestAwareAuthenticationSuccessHandler is correct since the redirection is done automatically.
Just add new attribute "always-use-default-target" to "form-login" tag and set it to "false" (by default it is set to "true", even if don't specify it) like this:
<form-login
login-page="/login" authentication-failure-url="/loginfail"
default-target-url="/login"
authentication-success-handler-ref="successHandler"
always-use-default-target="false"
/>
You can read more about it in Spring Security documentation here, on end of section: 3.2.3.
If you add this, you won't need custom onAuthenticationSuccess anymore and you won't need to store anything in session on login page.

Spring Security: put additional attributes(properties) in the session on success Authentication

Just simple question: what is the best way to add attributes(properties) to the HttpSession on success authentication? The userID for example.
For now i'm using my own SimpleUrlAuthenticationSuccessHandler implementation in UsernamePasswordAuthenticationFilter and doing it like this:
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication auth)
throws IOException, ServletException {
PersonBean person = (PersonBean) auth.getPrincipal();
request.getSession().setAttribute("currentUserId", person .getId().toString());
super.onAuthenticationSuccess(request, response, auth);
But I dont think this is good approach as there is another ways to do authentication(RememberMe for example).
So what do I need to use here?
The answer was given on spring forum. Link.
Generally, need to implement an ApplicationListener which listens for succes events and put additional attributes in the session there.
But in my case its not required to store attributes in the session. I can retrieve userID like here:
var userId = ${pageContext.request.userPrincipal.principal.id}
Spring does all this for you, you'll have to create a table *persistent_logins*, here is a snippet from app context that might help. And the official doc's lay describe in detail what is required :
<security:http auto-config='true'>
<security:intercept-url pattern="/**" access="ROLE_USER" />
<security:form-login login-page="/Login"
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-url="/Login?login_error=1" />
<security:remember-me data-source-ref="dataSource"
user-service-ref="myUserService" />
</security:http>
and then you can access the principal object from your anywhere in your app, eg below shows the tag to output username in jsp :
<sec:authentication property="principal.username" />
and from your java code this can be done :
MyUser user = (MyUser) authentication.getPrincipal();

How to retrieve pK using spring security

I implement this method of the UserDetailService interface,
public UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException, DataAccessException {
final EmailCredential userDetails = persistentEmailCredential
.getUniqueEmailCredential(username);
if (userDetails == null) {
throw new UsernameNotFoundException(username + "is not registered");
}
final HashSet<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
for (UserRole role:userDetails.getAccount().getRoles()) {
authorities.add(new GrantedAuthorityImpl(role.getRole()));
}
return new User(userDetails.getEmailAddress(), userDetails
.getPassword(), true, true, true, true,
authorities);
}
In the security context I do some thing like this:
<!-- Login Info -->
<form-login default-target-url='/dashboard.htm' login-page="/login.htm"
authentication-failure-url="/login.htm?authfailed=true"
always-use-default-target='false' />
<logout logout-success-url="/login.htm" invalidate-session="true" />
<remember-me user-service-ref="emailAccountService" key="fuellingsport" />
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>
Now I want to pop out the Pk of the logged in user.
How can I show it in my jsp pages?
Any idea?
I guess you are looking for something like this, which can be used in jsp.
<security:authentication property="principal.username"/>
You can implement your own class extending User with required fields and return it from UserDetailsService.
Then you can access these fields as pointed out by Raghuram:
<security:authentication property="principal.pk"/>

Resources