How to configure jdbc authentication manager in spring security using java and xml configuration?

你。 提交于 2019-12-23 05:32:18

问题


I’m trying to secure spring boot web application using spring security, but I’m getting confused with cascading methods while configuring authentication manager. Currently, I’m using in-memory database, which has tables users, authorities populated with data.

Can anyone please explain easier way to configure authentication mechanism for this use case?


回答1:


For the benefit of others who is in need of this.

1. Jdbc Authentication

For implementing jdbcAuthentication you need to write two queries to your database tables
Query1: usersByUsernameQuery (Sets the query to be used for finding a user by their username.)
Query2: authoritiesByUsernameQuery (Sets the query to be used for finding a user's authorities by their username.)

Either it may be java configuration or xml configuration, all you need to do is
1. Create datasource
2. Configure jdbc authentication in AuthenticationManagerBuilder by injecting datasource dependency and configuring usersByUsernameQuery and authoritiesByUsernameQuery.
3. Configure HttpSecurity. Details and defaults given below
----- Configure intercept-url pattern and authorization for those url's

Default role of unauthenticated user = ROLE_ANONYMOUS

----- Configure form login to avoid default login screen and default behavior given below.

login-page        = "/login" with HTTP get
usernameParameter = "username"
passwordParameter = "password"
failureUrl        = "/login?error"
loginProcessingUrl= "/login" with HTTP post
successUrl        = "/"

----- Configure logout to override default behavior.

logoutUrl        = "/logout"
logoutSuccessUrl = "/login?logout"

----- Configure session management to override default behavior

expiredUrl         = "/login?expired"
invalidate-session = true //you can set false and use delete-cookies="JSESSIONID"
maximumSessions    = The default is to allow any number of sessions for a users.

Javaconfig way

If this is not sufficient download from my github repository. Working copy of Spring security with Java configuration

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter 
{

    public DataSource dataSource()
    {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springmvc");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setInitialSize(2);
        dataSource.setMaxActive(5);

        return dataSource;
    }


    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.jdbcAuthentication().dataSource(dataSource()).passwordEncoder(passwordEncoder())
                .usersByUsernameQuery("select username, password, enabled from userdetails where userName=?")
                .authoritiesByUsernameQuery(
                        "select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId  where username = ?");
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception
    {
        http
        .authorizeRequests()
            .antMatchers("/resources/**", "/", "/login", "/api/**").permitAll()
            .antMatchers("/config/*", "/app/admin/*")
            .hasRole("ADMIN")
            .antMatchers("/app/user/*")
            .hasAnyRole("ADMIN", "USER")
        .and().exceptionHandling()
            .accessDeniedPage("/403")
        .and().formLogin()
            .loginPage("/login")
            .usernameParameter("userName").passwordParameter("password")
            .defaultSuccessUrl("/app/user/dashboard")
            .failureUrl("/login?error=true")
        .and().logout()
            .logoutSuccessHandler(new CustomLogoutSuccessHandler())
            .invalidateHttpSession(true)
        .and()
            .csrf()
                .disable();

        http.sessionManagement().maximumSessions(1).expiredUrl("/login?expired=true");
    }

    @Bean
    public PasswordEncoder passwordEncoder() 
    {
        return new BCryptPasswordEncoder();
    }

}

XML configuration way (not for spring boot)

If this is not sufficient download from my github repository. Working copy of Spring security with XML configuration

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd  
    http://www.springframework.org/schema/security  
    http://www.springframework.org/schema/security/spring-security.xsd">  

    <http auto-config="true" use-expressions="true" create-session="ifRequired">
        <csrf disabled="true"/>

        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/" access="permitAll" />
        <intercept-url pattern="/login" access="permitAll" />
        <intercept-url pattern="/api/**" access="permitAll" />

        <intercept-url pattern="/config/*" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/app/admin/*" access="hasRole('ROLE_ADMIN')" />

        <intercept-url pattern="/app/user/*" access="hasAnyRole('ROLE_USER', 'ROLE_ADMIN')" />

        <access-denied-handler error-page="/403" />

        <form-login 
            login-page="/login" 
            default-target-url="/app/user/dashboard" 
            authentication-failure-url="/login?error=true" 
            username-parameter="userName"
            password-parameter="password" />

        <logout invalidate-session="false" success-handler-ref="customLogoutSuccessHandler"/>

        <session-management invalid-session-url="/login?expired=true">
            <concurrency-control max-sessions="1" />
        </session-management>

    </http>


    <authentication-manager>
      <authentication-provider>
        <password-encoder ref="encoder" /> 
        <jdbc-user-service data-source-ref="dataSource"
          users-by-username-query=
            "select username, password, enabled from userdetails where userName=?"
          authorities-by-username-query=
            "select ud.username as username, rm.name as role from userdetails ud INNER JOIN rolemaster rm ON rm.id = ud.roleId  where username = ?" />
      </authentication-provider>
    </authentication-manager>

    <beans:bean id="encoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
    <beans:bean id="customLogoutSuccessHandler" class="com.pvn.mvctiles.configuration.CustomLogoutSuccessHandler" />

    <beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
         <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
         <beans:property name="url" value="jdbc:mysql://localhost:3306/springmvc" />
         <beans:property name="username" value="root"/>
         <beans:property name="password" value="root"/>
         <beans:property name="initialSize" value="2" />
         <beans:property name="maxActive" value="5" />
    </beans:bean>       
</beans:beans>



2. Implement UserDetailsService in your Dao and inject UserDetailService in Authentication manager buider

Here you need to override loadUserByUsername method
1. load user password from database by username passed as arguement in the method.
2. Load authorities for user from database and construct a list of GrantedAuthority
3. Create User by passing password and authorities fetched in step 1 and step 2
4. Return UserDetail object so that authentication and authorization will be taken care by spring container itself.

@Component
public class UserDaoImpl implements UserDao, UserDetailsService
{

    Logger          OUT = LoggerFactory.getLogger(UserDaoImpl.class);

    @Autowired
    SessionFactory  sessionFactory;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
    {
        try (Session session = sessionFactory.openSession();)
        {

            CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
            CriteriaQuery<DbUserDetails> userCriteria = criteriaBuilder.createQuery(DbUserDetails.class);
            Root<DbUserDetails> userRoot = userCriteria.from(DbUserDetails.class);
            userCriteria.select(userRoot).where(criteriaBuilder.equal(userRoot.get("userName"), username));

            Query<DbUserDetails> userQuery =session.createQuery(userCriteria);
            DbUserDetails dbUser = userQuery.getSingleResult();

            CriteriaQuery<RoleMaster> roleCriteria = criteriaBuilder.createQuery(RoleMaster.class);
            Root<RoleMaster> roleRoot = roleCriteria.from(RoleMaster.class);
            roleCriteria.select(roleRoot).where(criteriaBuilder.equal(roleRoot.get("id"), dbUser.getRoleId()));

            Query<RoleMaster> roleQuery =session.createQuery(roleCriteria);
            RoleMaster role = roleQuery.getSingleResult();

            List<GrantedAuthority> authList = new ArrayList<>();
            authList.add(new SimpleGrantedAuthority(role.getName()));

            return new User(username, dbUser.getPassword(),true, true, true, true, authList);
        }
        catch (Exception e)
        {
            OUT.error("Exception - {}", e);
            throw new UsernameNotFoundException("Exception caught", e);
        }
    }
}
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter 
{   
    @Autowired
    UserDaoImpl userDaoImpl;

    @Autowired
    public void configureUserDetailsService(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(userDaoImpl);
    }
    ...
}



回答2:


Recently, while learning Spring MVC, I did this. Hope this helps!

LoginController.java

LoginController to handle all login related web requests. It contains a single request mapping method to handle login failure and logout requests. Since the request mapping method returns a view named login, we need to create login.jsp.

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login() {
        return "login";
    }
}

login.jsp

Important tags in login.jsp. Here, we simply check whether the page request parameter contains a variable called error, if contains we show error message. Similalry, for logout and accessDenied.

<c:if test="${param.error != null}">
  <div class="alert alert-danger">
   <p>Invalid username and password.</p>
  </div>
</c:if>

We post login form values, username and password, to the Spring security authentication handler UR, which is stored in a variable called ${loginUrl}. Here <c:url> is used to encode the URL.

<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post" class="form-horizontal">

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Products</title>
    <link rel="stylesheet"
          href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<div class="container">
        <div class="row">
            <div class="col-md-4 col-md-offset-4">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <h3 class="panel-title">Please sign in</h3>
                    </div>
                    <div class="panel-body">
                        <c:url var="loginUrl" value="/login" />
                        <form action="${loginUrl}" method="post" class="form-horizontal">
                            <c:if test="${param.error != null}">
                                <div class="alert alert-danger">
                                    <p>Invalid username and password</p>
                                </div>
                            </c:if>

                            <c:if test="${param.logout != null}">
                                <div class="alert alert-success">
                                    <p>You have been logged out successfully</p>
                                </div>
                            </c:if>

                            <c:if test="${param.accessDenied != null}">
                                <div class="alert alert-danger">
                                    <p>Access Denied: You are not authorized</p>
                                </div>
                            </c:if>

                            <div class="input-group input-sm">
                                <label class="input-group-addon" for="userId">
                                    <i class="fa fa-user"></i>
                                </label>
                                <input type="text" class="form-control" id="userId" name="userId"
                                       placeholder="Enter username" required>
                            </div>

                            <div class="input-group input-sm">
                                <label class="input-group-addon" for="password">
                                    <i class="fa fa-lock"></i>
                                </label>
                                <input type="password" class="form-control" id="password" name="password"
                                       placeholder="Enter password" required>
                            </div>

                            <div class="form-actions">
                                <input type="submit" class="btn btn-block btn-primary btn-default"
                                       value="Login">
                            </div>

                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

SecurityConfig.java

We created LoginController to dispatch the login page. We need to tell Spring to present this login page to users if they try to access a page without logging in. To enforce that, WebSecurityConfigurerAdapter class comes in; by extending WebSecurityConfigurerAdapter, we can configure the HttpSecurity object for various security-related settings in web application. So SecurityConfig class is created to configure security-related aspects of web application.

One of the important methods in the SecurityConfig class is configureGlobalSecurity; under this method we are simply configuring AuthenticationManagerBuilder to create two users john and admin, with a specified password and roles.

The next important method is configure; within this method we are doing some authentication and authorisation related configuration. The first configuration tells Spring MVC that it should redirect the users to the login page if authentication is required; here the loginpage attribute denotes to which URL it should forward the request to get the login form. (The request path should be same as the request mapping of the login() method of LoginController. We also set the username and password parameter name in this configuration.

With this configuration, while posting the username and password to the Spring Security authentication handler through the login page, Spring expects those values to be bound under the variable name userId and password respectively; that's why the input tags for username and password carry the name attributes userId and password

<input type="text" class="form-control" id="userId" name="userId"
placeholder="Enter Username" required>
<input type="password" class="form-control" id="password" name="password"
placeholder="Enter Password" required>

Similarly, Spring handles the logout operation under the /logout URL.

Next we configure default success URL, which denotes the default landing page after a successful login; similarly failure URL indicates to which the URL the request needs to be forwarded in the case of login failure.

httpSecurity.formLogin().defaultSuccessUrl("/app/user/dashboard").failureUrl("/login?error");

We are setting the request parameter to error in the failure URL; when login page is rendered, it will show the error message "Invalid username and password" in case of logi failure. Similarly, we can also configure logout success URL, which denotes where the request needs to be forwarded to after logout.

httpSecurity.logout().logoutSuccessUrl("/login?logout");

Similarly, for accessDenied.

The next configuration defines which user should get access to which page.

httpSecurity.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/**/add").access("hasRole('ADMIN')")
.antMatchers("/**/app/**").access("hasRole('USER')");

The preceding configuration defines three important authorization rules for our web application, in terms of Ant pattern matchers. The first one allows the request URLs that end with /, even if the request doesn't carry any roles. The next rule allows all request URLs that end with /add, if the request has the role ADMIN. The third rule allows all request URLs that have the path /app/ if they have the role USER.


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder managerBuilder) throws Exception {
        managerBuilder.inMemoryAuthentication().withUser("john")
                .password("{noop}pa55word").roles("USER");
        managerBuilder.inMemoryAuthentication().withUser("admin")
                .password("{noop}root123").roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.formLogin().loginPage("/login").usernameParameter("userId")
                .passwordParameter("password");
        httpSecurity.formLogin().defaultSuccessUrl("/app/user/dashboard")
                .failureUrl("/login?error");
        httpSecurity.logout().logoutSuccessUrl("/login?logout");
        httpSecurity.exceptionHandling().accessDeniedPage("/login?accessDenied");
        httpSecurity.authorizeRequests().antMatchers("/").permitAll()
                .antMatchers("/**/add").access("hasRole('ADMIN')")
                .antMatchers("/**/app/**").access("hasRole('USER')");
        httpSecurity.csrf().disable();
    }
}

SecurityWebApplicationInitializer.java

After defining security-related configurations in the security file, Spring should know about this configuration file and will have to read this configuration file before booting the application. Only then can it create and manage those security-related configurations. To pick up this file by Spring during bootup, we created SecurityWebApplicationInitializer class extending AbstractSecurityWebApplicationInitializer class.

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}

somePage.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>


<a href="<c:url value='/logout' />">Logout</a>


来源:https://stackoverflow.com/questions/53455441/how-to-configure-jdbc-authentication-manager-in-spring-security-using-java-and-x

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!