1

我正在尝试使用 Spring Security 保护 Spring Boot Web 应用程序,但是在配置身份验证管理器时我对级联方法感到困惑。目前,我正在使用内存数据库,其中包含表用户、填充数据的权限。

有没有更简单的方法来为此用例配置身份验证机制?

4

2 回答 2

1

最近,在学习 Spring MVC 时,我这样做了。希望这可以帮助!

登录控制器.java

LoginController 处理所有与登录相关的 Web 请求。它包含一个单一的请求映射方法来处理登录失败和注销请求。由于请求映射方法返回一个名为 login 的视图,我们需要创建 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";
    }
}

登录.jsp

login.jsp 中的重要标记。在这里,我们只是检查页面请求参数是否包含一个名为error的变量,如果包含我们显示错误消息。类似地,注销和访问被拒绝。

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

我们将登录表单值、用户名和密码发布到 Spring 安全身份验证处理程序 UR,该处理程序存储在一个名为${loginUrl}. 这里<c:url>用于对 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>

安全配置.java

我们创建LoginController了发送登录页面。我们需要告诉 Spring 如果用户尝试在没有登录的情况下访问页面,则向他们展示这个登录页面。为了强制执行,WebSecurityConfigurerAdapter类进来了;通过扩展WebSecurityConfigurerAdapter,我们可以HttpSecurity为 Web 应用程序中的各种安全相关设置配置对象。因此创建 SecurityConfig 类来配置 Web 应用程序的安全相关方面。

类中的重要方法之一SecurityConfigconfigureGlobalSecurity;在这种方法下,我们只需配置AuthenticationManagerBuilder创建两个用户johnadmin,并使用指定的密码和角色。

下一个重要的方法是configure;在这个方法中,我们正在做一些身份验证和授权相关的配置。第一个配置告诉 Spring MVC 如果需要身份验证,它应该将用户重定向到登录页面;这里的loginpage属性表示它应该将请求转发到哪个 URL 以获取登录表单。(请求路径应该和 的login()方法的请求映射一致LoginController。我们在这个配置中也设置了用户名和密码参数名。

使用此配置,当通过登录页面将用户名和密码发布到 Spring Security 身份验证处理程序时,Spring 期望这些值分别绑定在变量名userId和变量名下password;这就是为什么用户名和密码的输入标签带有名称属性userIdpassword

<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>

同样,Spring 处理/logoutURL 下的注销操作。

接下来我们配置默认成功URL,表示登录成功后的默认登陆页面;同样 failure URL 表示在登录失败的情况下请求需要转发到哪个 URL。

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

我们正在将失败 URL 中的请求参数设置为错误;当登录页面被渲染时,它将显示错误消息“无效的用户名和密码”以防登录失败。同样,我们也可以配置注销成功URL,表示注销后请求需要转发到的地方。

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

Similarly, for accessDenied.

下一个配置定义了哪个用户应该访问哪个页面。

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

上述配置为我们的 Web 应用程序定义了三个重要的授权规则,就 Ant 模式匹配器而言。第一个允许以 / 结尾的请求 URL,即使请求不携带任何角色。如果请求具有 ADMIN 角色,则下一条规则允许所有以 /add 结尾的请求 URL。第三条规则允许所有具有路径 /app/ 的请求 URL,如果它们具有角色 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

在安全文件中定义了与安全相关的配置后,Spring 应该知道这个配置文件,并且必须在启动应用程序之前读取这个配置文件。只有这样,它才能创建和管理那些与安全相关的配置。为了在启动期间由 Spring 获取此文件,我们创建了SecurityWebApplicationInitializer类扩展AbstractSecurityWebApplicationInitializer类。

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

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}

一些页面.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>
于 2019-09-03T06:01:42.967 回答
1

为了其他有需要的人的利益。

1.jdbc认证

要实现 jdbcAuthentication,您需要向数据库表
Query1 编写两个查询: usersByUsernameQuery(设置用于通过用户名查找用户的查询。)
Query2: authoritiesByUsernameQuery(设置用于通过用户名查找用户权限的查询.)

它可能是java配置或xml配置,您需要做的就是
1.创建数据源2.通过注入数据源依赖项并配置usersByUsernameQuery和authoritiesByUsernameQuery来
配置jdbc身份验证。 3. 配置HttpSecurity。下面给出的详细信息和默认值 ----- 为这些 url 配置拦截 url 模式和授权AuthenticationManagerBuilder


Default role of unauthenticated user = ROLE_ANONYMOUS

----- 配置表单登录以避免默认登录屏幕和下面给出的默认行为

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

----- 配置注销以覆盖默认行为。

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

----- 配置会话管理以覆盖默认行为

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方式

如果这还不够,请从我的 github 存储库下载。带有 Java 配置的 Spring 安全工作副本

@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配置方式(不用于spring boot)

如果这还不够,请从我的 github 存储库下载。带有 XML 配置的 Spring 安全工作副本

<?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. 在你的 Dao 中实现 UserDetailsS​​ervice 并在 Authentication manager buider 中注入 UserDetailService

在这里您需要覆盖 loadUserByUsername 方法1.通过在方法中作为参数传递的用户名从数据库
加载用户密码。
2. 从数据库中加载用户的权限并构造一个 GrantedAuthority 列表
3. 通过传递密码和在步骤 1 和步骤 2 中获取的权限来创建用户
4. 返回 UserDetail 对象,以便 Spring 容器本身负责身份验证和授权。

@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);
    }
    ...
}
于 2019-09-03T03:19:34.200 回答