让我来详细讲解一下如何利用Spring Security构建REST服务实现记住我(remember-me)功能。
什么是记住我功能?
记住我是一个常见的Web应用程序功能,允许用户在关闭并重新打开浏览器后继续使用应用程序而无需重新登录。通常,当用户登录时,他们可以选择“记住我”选项。如果选中此选项,则应用程序将在用户关闭并重新打开浏览器时,使用之前提供的凭据进行自动登录。
实现过程
下面将分为5步来实现Spring Security的记住我功能。
1. 添加Spring Security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2. 配置Spring Security
在这一步中,需要配置Spring Security。首先,要启用remember-me功能:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.rememberMe(); // 启用remember-me功能
}
}
上述代码通过rememberMe()
启用了Spring Security的remember-me功能。这将启用Cookie的自动创建和使用,以保存已认证的用户之间的状态。
3. 配置“记住我”参数
Spring Security提供了以下“记住我”参数的配置:
- key:用于加密令牌的密钥,必须是16个字符以上的字符串。
- tokenValiditySeconds:令牌的有效期,以秒为单位。默认为2周。
- rememberMeParameter:处理“记住我”请求的参数名。默认为"remember-me"。
- rememberMeCookieName:用于保存令牌的Cookie的名称。默认为“remember-me”。
- useSecureCookie:设置是否只发送安全Cookie。默认为false。
- alwaysRemember:设置是否始终发送“记住我”Cookie,而不是在默认的会话Cookie上添加一个属性。默认为false。
你可以通过rememberMe()
方法进行配置:
http
.rememberMe()
.key("my-remember-me-key")
.tokenValiditySeconds(86400) // 一天
.rememberMeParameter("remember-me")
.rememberMeCookieName("my-remember-me-cookie")
.useSecureCookie(true)
.alwaysRemember(true);
4. 编写登录页面和登录处理程序
在登录页面中包含一个复选框,用户可以使用该复选框选择是否记住他们的登录信息。在登录处理程序中,将使用用户名、密码和remember-me参数来进行身份验证。
以下是一个简单的登录页面和应用程序的登录处理程序:
<html>
<body>
<h2>Login</h2>
<form action="/login" method="post">
<p>
<label for="username">Username:</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password:</label>
<input type="password" id="password" name="password"/>
</p>
<p>
<label for="remember-me">Remember me:</label>
<input type="checkbox" id="remember-me" name="remember-me"/>
</p>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
@Controller
public class LoginController {
@GetMapping("/login")
public String showLoginPage() {
return "login";
}
@PostMapping("/login")
public String login(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
String password = request.getParameter("password");
String rememberMe = request.getParameter("remember-me");
if (username.equals("admin") && password.equals("password")) {
// 如果成功验证,则创建一个remember-me Cookie
String rememberMeToken = UUID.randomUUID().toString();
Cookie rememberMeCookie = new Cookie("remember-me", rememberMeToken);
rememberMeCookie.setPath("/");
if (rememberMe != null && rememberMe.equals("on")) {
rememberMeCookie.setMaxAge(60 * 60 * 24 * 30); // 一个月
} else {
rememberMeCookie.setMaxAge(0);
}
response.addCookie(rememberMeCookie);
return "success";
} else {
return "error";
}
}
}
在上述代码中,login()
方法将根据提供的用户名和密码进行身份验证。如果验证成功,则创建一个新的记住我Cookie,并将其添加到HTTP响应中。
5. 验证记住我功能是否正常
在每次访问应用程序时,Spring Security将使用保存在Cookie中的信息来自动登录用户。你可以使用类似于以下Java代码的方法验证它是否正常工作:
public void authenticateWithCookie() {
RestTemplate restTemplate = new RestTemplate();
// 创建一个HTTP头,包含一个"remember-me" Cookie
HttpHeaders headers = new HttpHeaders();
headers.set("Cookie", "remember-me=my-remember-me-token");
// 调用受Spring Security保护的REST服务
ResponseEntity<String> response = restTemplate.exchange(
"http://localhost:8080/protected",
HttpMethod.GET,
new HttpEntity<String>(headers),
String.class);
String responseBody = response.getBody();
System.out.println(responseBody);
}
这个方法将创建一个HTTP头,其中包含一个记住我Cookie。然后,它将调用受Spring Security保护的REST服务,以验证该Cookie是否有效。
示例
示例一:基础REST服务
首先,我们创建一个名为“demo”的Spring Boot项目,并在其中添加以下依赖项:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
接下来,我们可以创建一个名为“HelloController”的REST服务:
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "Hello, world!";
}
@GetMapping("/protected")
public String protectedEndpoint() {
return "This is a protected endpoint!";
}
}
这将为根路径和受保护的路径提供REST服务。
现在,我们可以在Spring Security配置文件中启用记住我功能:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.rememberMe();
}
}
最后,在“login.html”的编写包含“记住我”复选框的登录页面:
<html>
<body>
<h2>Login</h2>
<form action="/login" method="post">
<p>
<label for="username">Username:</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password:</label>
<input type="password" id="password" name="password"/>
</p>
<p>
<label for="remember-me">Remember me:</label>
<input type="checkbox" id="remember-me" name="remember-me"/>
</p>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
我们的示例一完成了!你现在可以启动应用程序并进行测试。
示例二:用户名密码都存储在数据库
我们还可以使用JDBC存储用户名和密码。首先,我们需要在pom.xml中添加以下依赖项:
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
接下来,我们可以在application.properties中配置我们的数据源:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=create-drop
最后,在Spring Security配置文件中稍作修改:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository()) // 设定tokenRepository
.tokenValiditySeconds(200000); // 设定token有效期
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"SELECT username, password, enabled FROM users WHERE username=?")
.authoritiesByUsernameQuery(
"SELECT username, authority FROM authorities WHERE username=?")
.passwordEncoder(new BCryptPasswordEncoder());
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
}
在这里,我们使用tokenRepository()
方法将JdbcTokenRepositoryImpl
注入到Spring Security中。这个类是可以持久化记住我的Token的,其默认的数据库表结构是:
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
);
最后,我们需要创建一些用户并赋予权限以供测试。
示例完整代码请参见:https://github.com/Bright-Sky/Spring-Security-Demo。
总结
在本篇文章中,我们讲解了如何使用Spring Security构建Rest服务实现“记住我”(remember-me)功能。我们通过示例说明如何启动基本的REST服务,并使用JDBC存储用户名和密码。如果你想了解更多有关Spring Security的内容,请访问官方文档:https://spring.io/projects/spring-security。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security 构建rest服务实现rememberme 记住我功能 - Python技术站