I am currently working on a java based invoice app.
What I need is , to auth a user with multiple Roles, e.g Peter should have role ROLE_SETTING,ROLE_REPORT,ROLE_QUOTE, while Anne only has one role ROLE_SETTING etc...
Here is my spring-security-config.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.2.xsd">
<http auto-config="true">
<access-denied-handler error-page="/403page" />
<intercept-url pattern="/makebill*" access="ROLE_ADMIN,ROLE_MAKEBILL" />
<intercept-url pattern="/report*" access="ROLE_ADMIN,ROLE_REPORT" />
<intercept-url pattern="/stock*" access="ROLE_ADMIN,ROLE_STOCK" />
<intercept-url pattern="/customer*" access="ROLE_ADMIN,ROLE_CUSTOMER" />
<!-- <intercept-url pattern="/setting*" access="ROLE_ADMIN,ROLE_SETTING" /> -->
<intercept-url pattern="/mainmenu*" access="ROLE_ADMIN,ROLE_MAKEBILL,ROLE_SETTING,ROLE_CUSTOMER,ROLE_REPORT,ROLE_STOCK" />
<form-login
login-page='/login' username-parameter="username"
password-parameter="password" default-target-url="/mainmenu"
authentication-failure-url="/login?authfailed" />
<logout logout-success-url="/login?logout" />
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service
data-source-ref="dataSource"
users-by-username-query="select username,password, enabled from person where username=?"
authorities-by-username-query="select username, role from person, role where username =? " />
</authentication-provider>
</authentication-manager>
</beans:beans>
the authentication-manager currently working fine with just one role, what i mean is...when the sql query authorities-by-username-query="select username, role from person, role where username =? " runs, if the returned value is just one role, e.g ROLE_REPORT, the application works fine, but if have a record in db like, ROLE_REPORT,ROLE_SETTING,ROLE_CUSTOMER , when the query retrieves this value, the application will return me 404 error, it seems I won't be able to grant a user with multiple roles.
Can anyone please point me out what I have done wrong.
Thanks.
I found a solution myself, implemented my own UserDetails, here are the code
#Service("assembler")
public class Assembler {
#Transactional(readOnly = true)
User buildUserFromUserEntity(Person person) {
String username = person.getUsername();
String password = person.getPassword();
boolean enabled = true;
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
String[] authStrings = person.getRole().split(",");
for(String authString : authStrings) {
System.out.println("all auth roles: " + authString);
authorities.add(new SimpleGrantedAuthority(authString));
}
User user = new User(username, password, enabled,
true, true, true, authorities);
return user;
}
}
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired private PersonService personService;
#Autowired private Assembler assembler;
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
UserDetails userDetails = null;
Person person = null;
List<Person> listPeople = personService.listPeople();
for (Person p: listPeople) {
if (p.getUsername().equals(username)) {
person = p;
}
}
if (person == null)
throw new UsernameNotFoundException("user not found");
return assembler.buildUserFromUserEntity(person);
}
}
and here is the spring-security-config.xml
<http auto-config="true">
<access-denied-handler error-page="/403page" />
<intercept-url pattern="/makebill*" access="ROLE_ADMIN,ROLE_MAKEBILL" />
<intercept-url pattern="/report*" access="ROLE_ADMIN,ROLE_REPORT" />
<intercept-url pattern="/stock*" access="ROLE_ADMIN,ROLE_STOCK" />
<intercept-url pattern="/customer*" access="ROLE_ADMIN,ROLE_CUSTOMER" />
<intercept-url pattern="/setting*" access="ROLE_ADMIN,ROLE_SETTING" />
<intercept-url pattern="/mainmenu*" access="ROLE_ADMIN,ROLE_MAKEBILL,ROLE_SETTING,ROLE_CUSTOMER,ROLE_REPORT,ROLE_STOCK" />
<form-login
login-page='/login' username-parameter="username"
password-parameter="password" default-target-url="/mainmenu"
authentication-failure-url="/login?authfailed" />
<logout logout-success-url="/login?logout" />
</http>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
<beans:bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<beans:property name="providers">
<beans:list>
<beans:ref local="daoAuthenticationProvider" />
</beans:list>
</beans:property>
</beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
</authentication-provider>
</authentication-manager>
I am now able to assign multiple roles to a single user perfectly.
Thanks for reading :)
I am facing a weird problem, When I write
<intercept-url pattern="/user/**" access="hasRole('ROLE_USER')" />
it works fine but I change it to
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
it goes directly to 403 page.
Here is my spring security configuration:
<http pattern="/api/**" security="none"/>
<http pattern="/login" security="none"/>
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin*" access="hasRole('ROLE_ADMIN')"/>
<!-- access denied page -->
<access-denied-handler error-page="/403" />
<form-login login-page="/login" authentication-failure-url="/login?error"
username-parameter="username" password-parameter="password"
authentication-success-handler-ref="myAuthenticationSuccessHandler"/>
<logout logout-success-url="/login?logout" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<!-- enable csrf protection -->
<csrf />
</http>
<authentication-manager>
<authentication-provider user-service-ref="loginService" />
</authentication-manager>
My Custom UserDetailsService:
package com.dynamic.spring.service;
#Service("loginService")
public class LoginServiceImpl implements UserDetailsService {
#Autowired
LoginDao loginDao;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Users user = loginDao.findByUserName(username);
//problem
List<GrantedAuthority> authorities = buildUserAuthority(user
.getUserRole());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(Users user,
List<GrantedAuthority> authorities) {
return new User(user.getUser_email(), user.getPassword(),
user.isEnabled(), true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(
setAuths);
return Result;
}
and LoginDaoImp:
#Override
public Users findByUserName(String username) {
session = sessionFactory.openSession();
tx = session.getTransaction();
session.beginTransaction();
Users user = (Users) session.createQuery("From Users where User_email=:username")
.setParameter("username", username)
.uniqueResult();
tx.commit();
return user;
}
Kindly anyone help me I am stuck.
Thanks.
I want to have to types os users (admin and user)
This is my 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.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<http auto-config="true" use-expressions="true" access-denied-page="/jsp/static/deniedpage.jsp">
<intercept-url pattern="/login" access="permitAll" />
<intercept-url pattern="/jsp/static/**" access="isAuthenticated()" />
<intercept-url pattern="/jsp/users/**" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/jsp/admin/**" access="hasRole('ROLE_ADMIN')" />
<logout invalidate-session="true" logout-success-url="/login" logout-url="/logout" />
<form-login login-page="/login" default-target-url="/index" authentication-failure-url="/login?error=true" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select username,password, enabled from Users where USERNAME=?"
authorities-by-username-query="select u.username, ur.authority from USERS u, USER_ROLES ur where u.user_id= ur.user_id and u.username=?" />
</authentication-provider>
</authentication-manager>
<global-method-security secured-annotations="enabled" />
And this is my LoginController
#Controller
public class LoginController {
#RequestMapping(value = "/index", method = RequestMethod.GET)
public String accessUserPage(Model model) {
model.addAttribute("message",
"This page is publicly accessible. No authentication is required to view.");
return "/users/menu";
}
#RequestMapping("/admin/menuAdmin")
public String accessSecuredPage(Model model) {
model.addAttribute("message",
"Only you are authenticated and authorized to view this page.");
return "/admin/menuAdmin";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(ModelMap model) {
return "login";
}
#RequestMapping(value = "/loginfailed", method = RequestMethod.GET)
public String loginerror(ModelMap model) {
model.addAttribute("error", "true");
return "login";
}
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout(ModelMap model) {
return "login";
}
Now everytime i'm making the login he goes to that path /jsp/users/menu even if it is the Role_Admin
As you can see i have differents paths
<intercept-url pattern="/jsp/static/**" access="isAuthenticated()" />
<intercept-url pattern="/jsp/users/**" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/jsp/admin/**" access="hasRole('ROLE_ADMIN')" />
What's wrong with my code? The login works but not the way i want
Thanks
Your intercept-URL are wrong.
The pattern should not match the paths to the jsp file , but your URL path. You should write something like this .
<intercept-url pattern="/index" access="hasRole('ROLE_USER')">
<intercept-url pattern="/admin/menuAdmin" access="hasRole('ROLE_ADMIN')">
Hi I am new to Spring security 3 but with the help of this site I successfully developed Authentication and Authorization. Now I want to remove intercept-url pattern from my context-security.xml. I am looking to fetch authorities for specific user form database and create dynamically. Here i did...
in context-security.xml
<beans:bean id="filterSecurityInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<custom-filter before="FIRST" ref="requestMappings"/>
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="objectDefinitionSource" ref="requestMappings" />
</beans:bean>
<beans:bean id="requestMappings" class="com.nmmc.common.security.repository.RequestMappingFactoryBean" />
<http auto-config="true" once-per-request="false">
<!-- Restrict URLs based on role -->
<intercept-url pattern="/login.works" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/login/validate.works" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/css/*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<form-login login-page="/login.works" login-processing-url="/j_spring_security_check"
default-target-url="/login/validate.works"
authentication-failure-url="/login.works" />
<logout logout-url="/j_spring_security_logout"
logout-success-url="/login.works" invalidate-session="true" />
<session-management invalid-session-url="/login.works"
session-fixation-protection="none">
<concurrency-control max-sessions="50"
error-if-maximum-exceeded="true" />
</session-management>
</http>
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider"></authentication-provider>
</authentication-manager>
<beans:bean id="customAuthenticationProvider"
class="com.nmmc.common.security.repository.CustomAuthenticationProvider"></beans:bean>
in RequestMappingFactoryBean
import org.springframework.beans.factory.FactoryBean;
public class RequestMappingFactoryBean implements FactoryBean{
private final static String EOL = System.getProperty("line.separator");
public Object getObject() throws Exception
{
StringBuffer sb = new StringBuffer();
sb.append("CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON");
sb.append(EOL);
sb.append("PATTERN_TYPE_APACHE_ANT");
sb.append(EOL);
sb.append("/**login.works=IS_AUTHENTICATED_ANONYMOUSLY");
sb.append(EOL);
sb.append("/user/**=ROLE_ADMIN");
return sb.toString();
}
public Class getObjectType()
{
return String.class;
}
public boolean isSingleton()
{
return true;
}
}
Also when I remove ref from
<custom-filter before="FIRST" ref="requestMappings"/>
then it gives error that ref must be define also if i define it and run my app it gives error like
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Security namespace does not support decoration of element [custom-filter]
thats why i define as given.
Is there any changes in my code to run it?
Read the first answer at How to dynamically decide <intercept-url> access attribute value in Spring Security?
It has an outline of the solution.
I'm able to use the below to store a user in the SecurityContext and I get a valid Authentcation object (says im authenticated and has user_role attached) but then I still get routed to my /auth/login.html page. I stepped the code below and it is returning the "/registered/home.html" to JSF but for some reason when Spring applies the intercept rule to /registered/* it must see it as being an unauthenticated request. Any ideas?
#Named
#Scope("request")
public class SignUpDetail extends BaseAction{
#Inject
private SignUpDetailBean signUpDetailBean;
#Inject
private UserManager userManager;
#Inject #Named("am")
protected AuthenticationManager authenticationManager;
public String login(){
if(signUpDetailBean.getEmail() != null){
Users currentUser = userManager.getUser(signUpDetailBean.getEmail());
authenticateUserAndSetSession(currentUser, (HttpServletRequest) FacesUtils.getExternalContext().getRequest());
return "/registered/home.html";
}else{
return "/auth/login.html";
}
}
private void authenticateUserAndSetSession(Users user,
HttpServletRequest request)
{
UserDetails details = userManager.loadUserByUsername(user.getUsername());
UsernamePasswordAuthenticationToken usernameAndPassword =
new UsernamePasswordAuthenticationToken(
user.getUsername(), "pwd", details.getAuthorities());
// Authenticate, just to be sure
Authentication auth = authenticationManager.authenticate(usernameAndPassword);
// Place the new Authentication object in the security context.
SecurityContextHolder.getContext().setAuthentication(auth);
}
<context:annotation-config />
<context:component-scan base-package="dc" />
<global-method-security />
<http security="none" pattern="/javax.faces.resource/**" />
<http security="none" pattern="/services/rest-api/1.0/**" />
<http security="none" pattern="/preregistered/*" />
<http access-denied-page="/auth/denied.html">
<intercept-url
pattern="/**/*.xhtml"
access="ROLE_NONE_GETS_ACCESS" />
<intercept-url
pattern="/auth/**"
access="ROLE_ANONYMOUS,ROLE_USER" />
<intercept-url
pattern="/auth/*"
access="ROLE_ANONYMOUS" />
<intercept-url
pattern="/registered/*"
access="ROLE_USER" />
<intercept-url
pattern="/*"
access="ROLE_ANONYMOUS" />
<form-login
login-processing-url="/j_spring_security_check.html"
login-page="/auth/login.html"
default-target-url="/registered/home.html"
authentication-failure-url="/auth/login.html" />
<logout invalidate-session="true"
logout-success-url="/"
logout-url="/auth/logout.html"/>
<anonymous username="guest" granted-authority="ROLE_ANONYMOUS"/>
<remember-me user-service-ref="userManager" key="dfdf"/>
</http>
<!-- Configure the authentication provider -->
<authentication-manager alias="am">
<authentication-provider user-service-ref="userManager">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
Store the context object in session since you have set that particular url http security to none
...
HttpSession session = request.getSession();
...
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);