1

I am trying to implement token based authentication with spring security. Planning to use Header based authentication token.

My web.xml file is this

    <?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <listener> 
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener>

    <servlet>
        <servlet-name>api</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/api-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>api</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>login.html</welcome-file>
    </welcome-file-list>
    <session-config>
        <session-timeout>0</session-timeout>
    </session-config>
</web-app>

my admin servlet is 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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">

    <context:property-placeholder location="/WEB-INF/database.properties" />
    <context:component-scan base-package="com.netphenix.employee.api" />

    <tx:annotation-driven transaction-manager="hibernateTransactionManager"/>
    <mvc:annotation-driven />


    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="defaultContentType" value="text/html"/>
        <property name="ignoreAcceptHeader" value="true"/>
        <property name="favorPathExtension" value="true"/>
        <property name="order" value="1"/>
        <property name="mediaTypes">
            <map>
                <entry key="html" value="text/html"/>
                <entry key="json" value="application/json"/>
            </map>
        </property>
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
                <!-- Use tiles2 for views -->
                <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
                    <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
                </bean>
            </list>
        </property>
        <property name="defaultViews">
            <list>
                <ref bean="jsonView"/>
            </list>
        </property>
    </bean>

    <bean id="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
        <property name="contentType" value="application/json;charset=UTF-8"/>
    </bean>

    <bean id="dataSource"
          class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${database.driver}" />
        <property name="jdbcUrl" value="${database.url}" />
        <property name="user" value="${database.user}" />
        <property name="password" value="${database.password}" />
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}" />
        <property name="minPoolSize" value="${jdbc.minPoolSize}" />
        <property name="maxStatements" value="${jdbc.maxStatements}" />
        <property name="testConnectionOnCheckout" value="${jdbc.testConnection}" />
    </bean>

    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="packagesToScan" value="com.netphenix.employee.model"/>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>             
            </props>
        </property>
    </bean>

    <bean id="hibernateTransactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    </bean>
    <bean id="freemarkerConfigFactory" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
        <property name="templateLoaderPath" value="classpath:templates/"/>
    </bean>

<!--
    <bean id="daoAuthenticationProvider"
          class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="userDetailsService"/>
    </bean>

    <bean id= "authenticationManager" class= "org.springframework.security.authentication.ProviderManager">
        <constructor-arg>
            <list>
                <bean class= "org.springframework.security.authentication.dao.DaoAuthenticationProvider">
                    <property name="userDetailsService" ref="userDetailsService"/>
                </bean>
            </list>
        </constructor-arg>
    </bean>-->

        <sec:http>
        <sec:intercept-url pattern="/login.html"/>
        <sec:intercept-url pattern="/api/**" access="ROLE_ADMIN" />
        <sec:form-login login-page="/login.html"
                        authentication-failure-url="/login.html?error=failed"
                        login-processing-url="/login-please.html" />
        <sec:logout logout-url="/logoff-please.html"
                    logout-success-url="/logoff.html" />
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider user-service-ref="userDetailsService">
            <sec:password-encoder hash="md5"/>
        </sec:authentication-provider>
    </sec:authentication-manager>

    <sec:http auto-config="true">
        <sec:intercept-url pattern="/api/**" access="ROLE_ADMIN" />
        <sec:logout logout-success-url="/login" />
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="mkyong" password="password" authorities="ROLE_USER" />
                <sec:user name="eclipse" password="password" authorities="ROLE_ADMIN" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>
    <sec:global-method-security pre-post-annotations="enabled" />
</beans>

EDIT: UserDetailsService

@Service("userDetailsService") 
public class UserDetailsServiceImpl implements UserDetailsService {
  @Autowired
  private UserDao userDao;
  @Autowired 
  private Assembler assembler;

  @Transactional(readOnly = true)
  public UserDetails loadUserByUsername(String username)
      throws UsernameNotFoundException, DataAccessException {

    UserDetails userDetails = null;
//      User userEntity = userDao.getUser(username);
User userEntity = new User();
userEntity.setUsername("admin");
userEntity.setPassword("$2a$10$hbxecwitQQ.dDT4JOFzQAulNySFwEpaFLw38jda6Td.Y/cOiRzDFu");

    if (userEntity == null)
      throw new UsernameNotFoundException("user not found");

    return assembler.buildUserFromUserEntity(userEntity);
  }
}

API works okay. but its not getting authenticated. Which means, even for the url which requires ROLE_ADMIN also works without any authentication. Any pointer towards fixing this will be helpful.

CrazyProgrammer
  • 544
  • 8
  • 29
  • Is this a new project or a retrofit of an existing one? – chrylis -cautiouslyoptimistic- Nov 20 '19 at 19:04
  • This is a new project – CrazyProgrammer Nov 20 '19 at 19:06
  • @CrazyProgrammer Will you consider to post your userDetailsService code? What you have is fallback authentication, userDetailsService first and then your in memeory two users mkyong and eclipse. I think it is getting authenticated first in your userDetailsService. So If you post your userDetailsService code it will be helpful to suggest. Also you better enable log level to debug and post debug logs too. – PraveenKumar Lalasangi Nov 20 '19 at 19:12
  • @PraveenKumarLalasangi Added UserDetailsService – CrazyProgrammer Nov 20 '19 at 19:15
  • @PraveenKumarLalasangi username: admin password:admin@123 – CrazyProgrammer Nov 20 '19 at 19:16
  • @CrazyProgrammer you have hardcoded in userDetailsService. I think assembler is setting Authority as ROLE_ADMIN. – PraveenKumar Lalasangi Nov 20 '19 at 19:18
  • @CrazyProgrammer If you want quick test just return null from your userDetailsService so that it will consider next auth provider i.e, inMemory auth which is configured for two users mkyong and eclipse. Test it and let me know. – PraveenKumar Lalasangi Nov 20 '19 at 19:20
  • Then don't use this obsolete setup. Use Spring Boot (no `web.xml` or `beans.xml`) and probably Spring Security OAuth2. – chrylis -cautiouslyoptimistic- Nov 20 '19 at 19:22
  • @chrylis-onstrike- I have to use tomcat and spring-mvc, do you have links to any examples with OAuth2 ? – CrazyProgrammer Nov 20 '19 at 19:24
  • 1
    Don't use OAuth2 for API security, (Use JWT). it is completely for different requirement. I have explained security consideration here https://stackoverflow.com/a/58404641/2825798 – PraveenKumar Lalasangi Nov 20 '19 at 19:27
  • @PraveenKumarLalasangi good one, Do you have any sample code for JWT with spring-mvc? – CrazyProgrammer Nov 20 '19 at 19:30
  • @CrazyProgrammer Yes. I have. This stack overflow answer has link for my github repository you can refer this answer first then you can use easily the code from my repository. Also my respository has projects with xml based spring configuration, java based spring configuration examples, that you can use for compare and learn. If you are able to do it in java based configuration, it will be easier to switch to spring boot. Refer this answer https://stackoverflow.com/a/58894899/2825798 But this answer has hybrid requirement of achieving both web app security and rest api security. – PraveenKumar Lalasangi Nov 20 '19 at 19:38
  • 1
    Looks like you have what i wanted. Will give a try with your approach 2 – CrazyProgrammer Nov 20 '19 at 19:43
  • @PraveenKumarLalasangi JWT is a token format. OAuth2 is a framework for working with tokens. – chrylis -cautiouslyoptimistic- Nov 20 '19 at 19:46
  • @chrylis-onstrike- OAuth2 is advanced one, it comes with AuthorizationServer and ResourceServer Architecture, If there is no requirement of third party applications using Rest API then there is no need of going to Oauth2, Even though it provides Rest API access and security it has different architecture **where user authorizes for third party to have token on behalf of user to access users resource** this is OAuth2. – PraveenKumar Lalasangi Nov 20 '19 at 19:53

0 Answers0