如何使用 Java 和 XML 配置在 Spring Security 中配置 jdbc 身份验证管理器?
Posted
技术标签:
【中文标题】如何使用 Java 和 XML 配置在 Spring Security 中配置 jdbc 身份验证管理器?【英文标题】:How to configure jdbc authentication manager in Spring Security using Java and XML configuration? 【发布时间】:2019-04-26 13:59:17 【问题描述】:我正在尝试使用 Spring Security 保护 Spring Boot Web 应用程序,但在配置身份验证管理器时我对级联方法感到困惑。目前,我正在使用内存数据库,其中包含表用户、填充数据的权限。
有没有更简单的方法来为此用例配置身份验证机制?
【问题讨论】:
您是如何配置身份验证管理器的?你能提供更多关于你如何使用它的信息吗?请给我一些配置代码。 我正在扩展 WebSecurityConfigurerAdapter 并覆盖 configure(AuthenticationManagerBuilder authenticationManagerBuilder) 方法。 XML 或 Java 配置? Java 配置,我的项目中没有使用任何基于 xml 的配置 你用的是什么版本的spring security? 【参考方案1】:最近,在学习 Spring MVC 时,我这样做了。希望这会有所帮助!
LoginController.java
LoginController 处理所有与登录相关的网络请求。它包含一个单一的请求映射方法来处理登录失败和注销请求。由于请求映射方法返回一个名为 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";
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>
SecurityConfig.java
我们创建了LoginController
来调度登录页面。我们需要告诉 Spring 如果用户尝试在未登录的情况下访问页面,则向他们展示这个登录页面。为了强制执行,WebSecurityConfigurerAdapter
类进来了;通过扩展WebSecurityConfigurerAdapter
,我们可以配置HttpSecurity
对象,用于Web应用程序中的各种安全相关设置。因此创建 SecurityConfig 类来配置 Web 应用程序的安全相关方面。
SecurityConfig
类中的一个重要方法是configureGlobalSecurity
;在这种方法下,我们只是简单地配置AuthenticationManagerBuilder
来创建两个用户john
和admin
,具有指定的密码和角色。
下一个重要的方法是configure
;在这个方法中,我们正在做一些身份验证和授权相关的配置。
第一个配置告诉 Spring MVC 如果需要身份验证,它应该将用户重定向到登录页面;这里的loginpage
属性表示它应该将请求转发到哪个URL 以获取登录表单。 (请求路径应该和LoginController
的login()
方法的请求映射一致。我们在这个配置中也设置了用户名和密码参数名。
使用此配置,当通过登录页面将用户名和密码发布到 Spring Security 身份验证处理程序时,Spring 期望这些值分别绑定在变量名称 userId
和 password
下;这就是为什么用户名和密码的输入标签带有名称属性userId
和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>
同样,Spring 处理/logout
URL 下的注销操作。
接下来我们配置默认成功URL,表示登录成功后的默认登陆页面;同样的失败 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("nooppa55word").roles("USER");
managerBuilder.inMemoryAuthentication().withUser("admin")
.password("nooproot123").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
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>
【讨论】:
【参考方案2】:为了其他有需要的人的利益。
1。 Jdbc 认证
为了实现 jdbcAuthentication,您需要向数据库表写入两个查询Query1: usersByUsernameQuery(设置要使用的查询用于通过用户名查找用户。)Query2: authoritiesByUsernameQuery(设置用于按用户权限查找用户权限的查询用户名。)
可以是java配置,也可以是xml配置,只需要
1. 创建数据源
2.在AuthenticationManagerBuilder
中配置jdbc认证,注入datasource依赖,配置usersByUsernameQuery和authoritiesByUsernameQuery。
3. 配置HttpSecurity。下面给出的详细信息和默认值
----- 为这些 url 配置拦截 url 模式和授权
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.
Java配置方式
如果这还不够,请从我的 github 存储库下载。 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配置方式(不用于spring boot)
如果这还不够,请从我的 github 存储库下载。 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。在你的 Dao 中实现 UserDetailsService 并在 Authentication manager buider 中注入 UserDetailService
这里需要重写 loadUserByUsername 方法 1.通过在方法中作为参数传递的用户名从数据库中加载用户密码。 2.从数据库中为用户加载authorities,并构造一个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);
...
【讨论】:
以上是关于如何使用 Java 和 XML 配置在 Spring Security 中配置 jdbc 身份验证管理器?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 和 XML 配置在 Spring Security 中配置 jdbc 身份验证管理器?