Please refer the real issue why my #PreAuthorize("hasPermission(#user,'write')") is not working
Basically I'am trying to check a normal user
My controllerClass
package com.***.appconfig.controller;
import com.***.appconfig.dao.UserDaoImplementation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.***.appconfig.model.User;
import com.***.appconfig.security.CustomPermissionEvaluator;
#Controller
public class CheckPermissionController {
public static User user = new User();
UserDaoImplementation userDao = new UserDaoImplementation();
Boolean directPermission = false;
CustomPermissionEvaluator customPermissionEvaluator = new CustomPermissionEvaluator();
#RequestMapping("/checkPermission")
protected ModelAndView direct() throws Exception {
System.out.println("in direct");
user.setUserName("andrew");
userDao.addListValues(user);
System.out.println("before assign");
directPermission = userDao.assignUser(user);
System.out.print("after assign");
if (directPermission) {
return new ModelAndView("checkPermission");
} else {
return new ModelAndView("login");
}
}
}
Here is my Dao
import com.***.appconfig.model.User;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import java.util.HashMap;
#Component
public class UserDaoImplementation implements UserDao {
#Override
public User addListValues(User user) {
HashMap < String, String > permissionList = new HashMap < String, String > ();
permissionList.put("server", "write");
user.setPermissionList(permissionList);
return null;
}
#PreAuthorize("hasPermission(#user,'write')")
public Boolean assignUser(User user) {
System.out.println("in assign");
return true;
}
}
Here is my CustomPermissionEvaluator
package com.***.appconfig.security;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import com.***.appconfig.controller.CheckPermissionController;
import com.***.appconfig.model.User;
import com.***.appconfig.dao.UserDaoImplementation;
import java.io.Serializable;
import java.util.HashMap;
public class CustomPermissionEvaluator implements PermissionEvaluator {
public static User user;
public UserDaoImplementation userDao;
#Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
setPermissions();
String targetType = targetDomainObject.getClass().getSimpleName().toUpperCase();
HashMap < String, String > permissionList = user.getPermissionList();
System.out.print("before check");
if (permissionList.containsValue("write")) {
System.out.print("success check");
hasPermission = true;
}
return hasPermission;
}
#Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
Boolean hasPermission = false;
return hasPermission;
}
public void setPermissions() {
user.setUserName("andrew");
userDao.addListValues(user);
}
}
I created a duplicate user object inorder to dynmically populate in PermissionEvaluator.The hasPermission() overrride is not getting called.
Here is my spring-security.xml
<http auto-config="true">
<access-denied-handler error-page="/403page" />
<intercept-url pattern="/user" access="ROLE_USER" />
<intercept-url pattern="/admin" access="ROLE_ADMIN" />
<form-login login-page='/login' username-parameter="username" password-parameter="password" default-target-url="/user" authentication-failure-url="/login?authfailed" />
<logout logout-success-url="/login?logout" />
</http>
<global-method-security pre-post-annotations="enabled" secured-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
<authentication-manager>
<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 username, role from user_roles where username =? " />
</authentication-provider>
</authentication-manager>
<beans:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="permissionEvaluator" />
</beans:bean>
<beans:bean name="permissionEvaluator" class="com.coolminds.appconfig.security.CustomPermissionEvaluator" />undefined</beans:beans>
Your Controller class should inject all dependencies to make sure Spring can create appropriate proxy objects:
package com.***.appconfig.controller;
import com.***.appconfig.dao.UserDaoImplementation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.***.appconfig.model.User;
#Inject
UserDao userDao;
#Controller
public class CheckPermissionController {
#RequestMapping("/checkPermission")
protected ModelAndView direct() throws Exception {
User user = new User();
boolean directPermission = false;
System.out.println("in direct");
user.setUserName("andrew");
userDao.addListValues(user);
System.out.println("before assign");
directPermission = userDao.assignUser(user);
System.out.print("after assign");
if (directPermission) {
return new ModelAndView("checkPermission");
} else {
return new ModelAndView("login");
}
}
}
Related
I have been following DZONE example of how to create a simple web application with login and registration features using Spring MVC, but however I am struggling with one issue. Tomcat throws an exception in DAO Implementation class, saying
SEVERE: Servlet.service() for servlet [spring] in context with path [/PortfolioWebApp] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause java.lang.NullPointerException
I`ve tried changing spring config but nothing happened. Maybe someone could help me out with my problem.
This is DAO Implementation class:
package org.madness.java.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.madness.java.model.Login;
import org.madness.java.model.User;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class JdbcDaoImpl extends JdbcDaoSupport{
public User validateAccount(Login login) {
String sql = "SELECT * FROM Accounts WHERE USERNAME = '"+login.getUsername()+"' AND PASSWORD = '"+login.getPassword()+"'";
List<User> users = this.getJdbcTemplate().query(sql, new UserMapper());
if (users.size() > 0) {
return users.get(0);
}else {
return null;
}
}
private static class UserMapper implements RowMapper<User>{
#Override
public User mapRow(ResultSet rs, int arg1) throws SQLException {
User user = new User();
user.setId(rs.getLong("ID"));
user.setUsername(rs.getString("USERNAME"));
user.setPassword(rs.getString("PASSWORD"));
user.setFirstname(rs.getString("FIRSTNAME"));
user.setSurname(rs.getString("SURNAME"));
user.setEmail(rs.getString("EMAIL"));
user.setAddress(rs.getString("ADDRESS"));
return null;
}
}
}
This is Login Controller:
package org.madness.java.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.madness.java.model.User;
import org.madness.java.model.Login;
import org.madness.java.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class LoginController {
private UserService userService;
#RequestMapping(value="/login", method=RequestMethod.GET)
public ModelAndView showLogin(HttpServletRequest req, HttpServletResponse res) {
ModelAndView mav = new ModelAndView("login");
mav.addObject("login", new Login());
return mav;
}
#RequestMapping(value="/loginProcess", method=RequestMethod.POST)
public ModelAndView loginProcess(HttpServletRequest req, HttpServletResponse res, #ModelAttribute("login") Login login) {
ModelAndView mav = null;
if(login != null) {
userService = new UserService();
User user = userService.validateUser(login);
if(user != null) {
mav = new ModelAndView("home");
mav.addObject("firstname", user.getFirstname());
}else {
mav = new ModelAndView("login");
mav.addObject("message", "Error: Provided username or password are incorrect. Please, try again...");
}
}else {
mav = new ModelAndView("login");
mav.addObject("message", "Something happened inside DAO and Service!");
}
return mav;
}
}
This is Spring Config XML file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="org.madness.java" />
<context:annotation-config />
<bean name="userService" class="org.madness.java.service.UserService" />
<bean name="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.0.27:3306/spring" />
<property name="username" value="student" />
<property name="password" value="password" />
</bean>
<bean name="jdbcDaoImpl" class="org.madness.java.dao.JdbcDaoImpl">
<property name="dataSource" ref="datasource" />
</bean>
And this is UserService:
package org.madness.java.service;
import org.madness.java.dao.JdbcDaoImpl;
import org.madness.java.dao.UserDaoImpl;
import org.madness.java.model.Login;
import org.madness.java.model.User;
import org.springframework.beans.factory.annotation.Autowired;
public class UserService {
#Autowired
private JdbcDaoImpl dao;
public User validateUser(Login login) {
dao = new JdbcDaoImpl();
return dao.validateAccount(login);
}
}
You need to autowire UserService rather than instantiating it manually.
public class LoginController {
#Autowired
private UserService userService;
...
#RequestMapping(value="/loginProcess", method=RequestMethod.POST)
public ModelAndView loginProcess(HttpServletRequest req, HttpServletResponse res, #ModelAttribute("login") Login login) {
ModelAndView mav = null;
if(login != null) {
userService = new UserService(); //remove this !
Then the instance initialized by Spring will be used where the DAO is accessible etc.
I have configured spring basic authentication with custom authentication manager. My authentication manager will access database for user information. When Database is stopped and application trying to authenticate user, my application returns 401 (unauthorized). But my expected behavior is to return 500 (Internal server error) in this case. Below is how I have configured spring security.
<context:component-scan base-package="com.test.security" />
<sec:http use-expressions="true">
<sec:intercept-url pattern="/**" access="hasAnyRole('Admin','Data Operator','Data Collector')" />
<sec:http-basic />
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider
user-service-ref="myAuthenticationProvider">
<sec:password-encoder ref="encoder" />
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="myAuthenticationProvider"
class="com.test.security.MyUserDetailsService" />
<bean id="encoder"
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder">
</bean>
Below is the implementation of authenticate provider
package com.test.security;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.test.business.objects.Adminrole;
import com.test.business.objects.Adminuser;
import com.test.business.repository.AdminroleRepository;
import com.test.repository.AdminuserRepository;
#Service
public class MyUserDetailsService implements UserDetailsService{
#Autowired
private AdminuserRepository adminuserRepository;
#Autowired
private AdminroleRepository adminroleRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
//find admin user by user name
List<Adminuser> adminUsers = adminuserRepository.findByUsername(username);
Adminuser adminUser = adminUsers.get(0);
//find admin roles by user
List<Adminrole> adminRoles = adminroleRepository.getAdminRolesByUserId(adminUser.getUserid());
//create user details object
MyUserDetails userdetails = new MyUserDetails(adminUser, adminRoles);
return userdetails;
}
}
Take a look at this javadoc. It shows that UsernameNotFoundException is an AuthenticationException. My guess is that you are catching an exception when accessing the database and converting it to UsernameNotFoundException. Take a look at your database code, that's probably where the issue lies.
From my point of view this is the correct behavior for a resilient service. Why should the whole server break if just the authentication is broken? There may be public available resources of the server which should be available anyway even without a user database reachable. Even when a user logged in and got a token for example, why shouldn't he access protected resources as long as the token is valid? The Spring Security team did a great job here implementing it just in the way they did. You should not change the behavior.
I found a solution.
<context:component-scan base-package="com.test.security" />
<sec:http use-expressions="true">
<sec:intercept-url pattern="/**" access="hasAnyRole('Admin','Data Operator','Data Collector')" />
<sec:http-basic entry-point-ref="basicAuthenticationEntryPoint"/>
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider
user-service-ref="myAuthenticationProvider">
<sec:password-encoder ref="encoder" />
</sec:authentication-provider>
</sec:authentication-manager>
<bean id="myAuthenticationProvider"
class="com.test.security.MyUserDetailsService" />
<bean id="encoder"
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder">
</bean>
<bean id="basicAuthenticationEntryPoint"
class="com.test.MyAuthenticationEntryPoint"/>
package com.test.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionException;
#Component
public class MyAuthenticationEntryPoint extends BasicAuthenticationEntryPoint{
#Override
public void commence
(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
if (authEx.getCause() instanceof TransactionException){
response.addHeader("Internal Server Error", "Basic realm='" + getRealmName() + "'");
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}else{
response.addHeader("Authenticate-Faliure", "Basic realm='" + getRealmName() + "'");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("test");
super.afterPropertiesSet();
}
}
I'm not 100% sure I'm performing the correct request but I can't seem to get a token for a client. I've based my solution off of this tutorial 5 minutes with spring oauth 2.0.
I'm performing this request from postman:
/oauth/token?grant_type=client_credentials&client_id=mysupplycompany&client_secret=mycompanyk
That request responds with an exception:
org.springframework.security.authentication.InsufficientAuthenticationException:
There is no client authentication. Try adding an appropriate
authentication filter.
org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91)
Below are some files/classes you may need to help me figure out any issues.
WebInitializer
package com.squirrels.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;
public class WebInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
servletContext.addListener(new RequestContextListener());
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*");
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.squirrels.config");
return context;
}
}
PersistenceJPAConfig
package com.squirrels.config;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
#ComponentScan({ "com.squirrels.controller", "com.squirrels.services", "com.squirrels.persistence.dao", "com.squirrels.auth" })
#PropertySource(value = { "classpath:squirrel.properties" })
#ImportResource("classpath:spring-security.xml")
public class PersistenceJPAConfig {
#Autowired
private Environment environment;
#Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource());
liquibase.setDefaultSchema(environment.getRequiredProperty("db_schema"));
liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.xml");
return liquibase;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPersistenceUnitName("SquirrelAuth");
em.setPackagesToScan(new String[] { "com.squirrels.persistence.model" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("db_driverClass"));
dataSource.setUrl(environment.getRequiredProperty("db_jdbcUrl"));
dataSource.setUsername(environment.getRequiredProperty("db_user"));
dataSource.setPassword(environment.getRequiredProperty("db_password"));
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", environment.getRequiredProperty("db_hibernateDialect"));
return properties;
}
}
security-spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<sec:http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager">
<sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter"
before="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<sec:http pattern="/api/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/api/**" method="GET"
access="IS_AUTHENTICATED_FULLY" />
<sec:custom-filter ref="resourceServerFilter"
before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
</bean>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider
user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="clientDetails" class="com.squirrels.auth.OAuthClientDetailsImpl">
<property name="id" value="mysupplycompany" />
<property name="secretKey" value="mycompanykey" />
</bean>
<sec:authentication-manager id="userAuthenticationManager">
<sec:authentication-provider ref="customUserAuthenticationProvider" />
</sec:authentication-manager>
<bean id="customUserAuthenticationProvider" class="com.squirrels.auth.OAuthAuthenticationProvider">
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code />
<oauth:implicit />
<oauth:refresh-token />
<oauth:client-credentials />
<oauth:password authentication-manager-ref="userAuthenticationManager" />
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
resource-id="springsec" token-services-ref="tokenServices" />
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="120"></property>
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<mvc:annotation-driven />
</beans>
OAuthAuthenticationProvider
package com.squirrels.auth;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
public class OAuthAuthenticationProvider implements AuthenticationProvider {
#Autowired
private OAuthProxy proxy;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
boolean result = proxy.isValidUser("1", authentication.getPrincipal().toString(), authentication.getCredentials().toString());
if (result) {
List<GrantedAuthority> grantedAuthorities =
new ArrayList<GrantedAuthority>();
OAuthAuthenticationToken auth = new OAuthAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), grantedAuthorities);
return auth;
} else {
throw new BadCredentialsException("Bad User Credentials.");
}
}
#Override
public boolean supports(Class<?> arg0) {
return true;
}
}
WebMvcConfig
package com.squirrels.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
};
/*
* Configure ContentNegotiatingViewResolver
*/
#Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
// Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
resolvers.add(jsonViewResolver());
resolver.setViewResolvers(resolvers);
return resolver;
}
/*
* Configure View resolver to provide JSON output using JACKSON library to
* convert object in JSON format.
*/
#Bean
public ViewResolver jsonViewResolver() {
return new JsonViewResolver();
}
}
OAuthenticationToken
package com.squirrels.auth;
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
public class OAuthAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = -1092219614309982278L;
private final Object principal;
private Object credentials;
public OAuthAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
#Override
public Object getCredentials() {
return credentials;
}
#Override
public Object getPrincipal() {
return principal;
}
}
OAuthClientDetailsImpl
package com.squirrels.auth;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
public class OAuthClientDetailsImpl implements ClientDetailsService {
private String id;
private String secretKey;
#Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
if (clientId.equals(id))
{
List<String> authorizedGrantTypes = new ArrayList<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
authorizedGrantTypes.add("client_credentials");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId(id);
clientDetails.setClientSecret(secretKey);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
return clientDetails;
}
else {
throw new NoSuchClientException("No client recognized with id: " + clientId);
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
}
OAuthProxy
package com.squirrels.auth;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.squirrels.dto.UserDTO;
import com.squirrels.persistence.dao.UserDao;
import com.squirrels.persistence.model.UserModel;
import com.squirrels.services.UserUtil;
#Service
public class OAuthProxy {
protected static final Logger logger = LogManager.getLogger(OAuthProxy.class);
#Autowired
private UserDao userDaoImpl;
#Autowired
private UserUtil userUtil;
#Transactional
public boolean isValidUser(String organizationId, String username, String password) {
try{
UserModel user = userDaoImpl.findByOrganizationAndUserName(organizationId, username);
UserDTO userDto = userUtil.getDTO(user);
//TODO validate password (or other means of auth)
return true;
}catch(Exception e){
logger.error("No user: " + username + " found in organization: " + organizationId, e);
}
return false;
}
}
I had the same problem.
In my case is was because I mapped my DispatcherServlet like this :
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/dispatcher/*</url-pattern>
</servlet-mapping>
This was causing problem because security was configured on "/oauth/token", instead of "/dispatcher/oauth/token" :
<security:http pattern="/oauth/token" ...
So I just had to do :
<security:http pattern="/dispatcher/oauth/token"
And the problem was gone.
If that is not the case for you, it may be because you have no DelegatingFilterProxy configured.
Try configuring it in web.xml :
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And if you use java config instead of web.xml, extending the class org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer
will configure it.
I I am trying to use spring security to control authentication in my RMI client app.
I am using maven to load 3.1.3.RELEASE spring
Then in my client xml, I am seeing "authentication-provider is not allowed here" message.
This is the spring xml file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
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
">
<bean id="employeeService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
<property name="serviceUrl" value="rmi://localhost:1234/employee-service"/>
<property name="serviceInterface" value="rmi.common.EmployeeService"/>
<property name="refreshStubOnConnectFailure"><value>true</value></property>
<property name="remoteInvocationFactory"><ref bean="remoteInvocationFactory"/></property>
</bean>
<bean id="remoteInvocationFactory" class="org.springframework.security.remoting.rmi.ContextPropagatingRemoteInvocationFactory"/>
<security:authentication-manager alias="authManager">
<security:authentication-provider ref="customAuthenticationProvider"/>
</security:authentication-manager>
<bean id="customAuthenticationProvider" class="rmi.authentication.CustomAuthenticationProvider"/>
</beans>
This is my client side code:
package rmi.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import rmi.common.Employee;
import rmi.common.EmployeeService;
import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;
public class EmployeeClient {
#Resource(name = "authManager")
private org.springframework.security.authentication.AuthenticationManager authenticationManager; // specific for Spring Security
private static ApplicationContext ctx;
public static void main(String[] args) {
ctx = new ClassPathXmlApplicationContext("rmi-client-context.xml");
new EmployeeClient().exec();
}
public void exec() {
EmployeeService employee = (EmployeeService) ctx.getBean("employeeService");
Authentication authenticate = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken("myuser", "mypwd"));
if (authenticate.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticate);
}
employee.addEmployee(new Employee("Jack Su", "address1"));
employee.addEmployee(new Employee("Alice Liu", "address2"));
List<Employee> employees = employee.getEmployees();
System.out.println("Total number of employees: " + employees.size());
Iterator<Employee> it = employees.iterator();
while (it.hasNext()) {
Employee emp = (Employee) it.next();
System.out.println(" " + emp);
}
}
}
This is my testing authentication provider:
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.ArrayList;
import java.util.List;
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication argAuth) throws AuthenticationException {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(argAuth.getPrincipal(),
argAuth.getCredentials(), grantedAuths);
auth.setAuthenticated(true);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Not sure about your exact error, but something in your configuration might be wrong.
Your EmployeeClient is not a Spring-Managed-Bean, because you created it with new-operator, so your #Resource(name = "authManager") should not work. If you posted all your configuration authenticationManager.authenticate(..) should produce NullPointerException.
Instead of new you could do this in your Main.
EmployeeClient client = (EmployeeClient) ctx.getBean("employeeClient");
client.exec();
(and add #Service to your EmployeeClient).
I am trying to use Spring security for my web application. I am about to make a login feature, so users can login to secured pages in my website. So far I have been using Spring security's own , where I have used an in-memory data store for authentication (mostly to test if it worked, since I am still learning.)
My problem occurs, when I am trying to use my database where I have stored different Users. When I am trying to log in, spring security redirects me to my 'authentication-failure-url', instead of logging me in.
I have created a custom UserDetails class
package dk.chakula.web.security;
import dk.chakula.web.domain.User;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
*
* #author Martin Rohwedder
* #since 20-02-2013
* #version 1.0
*/
public class ChakulaUserDetails implements UserDetails {
private User user;
private List<? extends GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
public ChakulaUserDetails(User user, List<? extends GrantedAuthority> authorities) {
this.user = user;
this.authorities = authorities;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return this.user.getPassword();
}
#Override
public String getUsername() {
return this.user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
I have also created a custom UserDetailsService, which my 'authentication-provider' is referencing. That class looks like this.
package dk.chakula.web.security;
import dk.chakula.web.domain.User;
import dk.chakula.web.service.UserService;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
/**
*
* #author Martin Rohwedder
* #since 20-02-2013
* #version 1.0
*/
#Component("chakulaUserDetailsService")
public class ChakulaUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (StringUtils.isBlank(username)) {
throw new UsernameNotFoundException("Username was empty");
}
User user = userService.getUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User with username '" + username + "' was not found");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority(user.getUserRole()));
return new ChakulaUserDetails(user, grantedAuthorities);
}
}
My full spring security context looks like this
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
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">
<security:http use-expressions="true">
<!-- Form Login Filter -->
<security:form-login login-page="/login" default-target-url="/app/start" authentication-failure-url="/login?authenticationNok=1" username-parameter="username" password-parameter="password" always-use-default-target="true" />
<!-- Logout Filter -->
<security:logout logout-success-url="/home?logoutOk=1" logout-url="/logout" invalidate-session="true" />
<!-- Intercept Url filters -->
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/home" access="permitAll" />
<security:intercept-url pattern="/about" access="permitAll" />
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/resources/**" access="permitAll" />
<security:intercept-url pattern="/app/**" access="fullyAuthenticated" />
</security:http>
<bean id="chakulaUserDetailsService" class="dk.chakula.web.security.ChakulaUserDetailsService" />
<security:authentication-manager>
<security:authentication-provider user-service-ref="chakulaUserDetailsService">
<security:password-encoder hash="sha-256">
<security:salt-source user-property="username" />
</security:password-encoder>
<!--
<security:user-service>
<security:user name="test" password="e9233bd61e14137a7e28f92c50ce279215e774a1772d1e9dad5f275b9cc8177c" authorities="ROLE_CUSTOMER" />
</security:user-service>
-->
</security:authentication-provider>
</security:authentication-manager>
</beans>
You can get a look at all the source code at my Chakula project on GITHUB, if you need this to help me - https://github.com/martin-rohwedder/chakula
It all seemed to be a failure with my Dao Implementation class, because when I showed the message of the AuthenticationException it told me the following - could not resolve property: USERNAME of: dk.chakula.web.domain.User [from dk.chakula.web.domain.User u where u.USERNAME = :USERNAME]
All I had to do was to write u.USERNAME with non capital letters.
I rewrited my security context xml files form-login tags authentication-failure-url attribute with the following value instead.
<security:form-login login-page="/login" default-target-url="/app/start" authentication-failure-url="/login/failure" username-parameter="username" password-parameter="password" always-use-default-target="true" />
Thereafter I had in my AuthenticationController made a new method to render the failure view, and add the AuthenticationException message as a model to the view
#RequestMapping(value = {"/login/failure"}, method = RequestMethod.GET)
public ModelAndView renderLoginFailureView(HttpServletRequest request) {
ModelAndView mav = new ModelAndView("login");
AuthenticationException authEx = (AuthenticationException) request.getSession().getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
mav.addObject("authEx", authEx.getMessage());
return mav;
}
Next time I know to show all exception messages before rushing in to stackoverflow. Though now others maybe can benefit on my answer in the future.
Thanks to Arun P Johny for helping me out here.