Spring Security 支持各种来源的用户数据,包括内存、数据库、LDAP 等,它们被抽象为一个 UserDetailsService 接口,任何实现了 UserDetailsService 接口的对象都可以作为认证数据源。在这种设计模式下,Spring Security 显得尤为灵活。其中,基于内存的多用户支持的实现方式如下:
配置步骤
- 准备资源:创建Controller,并在Controller中建立一些测试路由。
- 资源授权的配置:使用 antMatchers() 方法进行 URL 匹配和权限设置。例如,antMatchers("/admin/api/")可以匹配 /admin/api/ 下的所有 API,并指定只有 ADMIN 角色才能访问。同理,/user/api/下的API可以设置为只有 USER 或 ADMIN 角色才能访问,而/app/api/下的API则可以设置为公开权限。
- 在配置文件中设置用户名和密码:在 application.properties 或 application.yml 中配置用户名、密码以及角色。
- 在 Security 配置类中配置用户存储:通过覆盖 configure(AuthenticationManagerBuilder auth) 方法,并使用 inMemoryAuthentication() 方法来配置基于内存的用户存储。可以使用withUser()方法来创建用户,并设置其密码和角色。
1、单个登录用户
配置单个登录用户,可以在项目的 application.properties 或 application.yml 中配置用户名和密码。
spring:security:user:name: panjunbiao #登录名称password: 123456 #登录密码
2、基于内存的多个登录用户
基于内存的用户存储不需要额外的数据库配置,适用于小型应用或测试环境。无需等待数据库连接或初始化,可以快速启动应用。
2.1 方式一:实现自定义 UserDetailsService 接口
实现自定义一个 UserDetailsService 接口,任何实现了 UserDetailsService 接口的对象都可以作为认证数据源。
/*** 内存中添加登录账号(方式一)*/
@Bean
public UserDetailsService userDetailsService()
{InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withUsername("admin").password("123456").roles("ADMIN").build());manager.createUser(User.withUsername("user").password("123456").roles("USER").build());manager.createUser(User.withUsername("panjunbiao").password("123456").roles("USER").build());return manager;
}/*** 由于5.x版本之后默认启用了委派密码编译器,* 因而按照以往的方式设置内存密码将会读取异常,* 所以需要暂时将密码编码器设置为 NoOpPasswordEncoder*/
@Bean
public PasswordEncoder passwordEncoder()
{return NoOpPasswordEncoder.getInstance();
}
InMemoryUserDetailsManager 是 UserDetailsService 接口中的一个实现类,它将用户数据源寄存在内存里,在一些不需要引入数据库这种重数据源的系统中很有帮助。
2.2 方式二:通过覆盖 configure(AuthenticationManagerBuilder auth) 方法
在 Security 配置类中配置用户存储,通过覆盖 configure(AuthenticationManagerBuilder auth) 方法,并使用 inMemoryAuthentication() 方法来配置基于内存的用户存储。可以使用withUser()方法来创建用户,并设置其密码和角色。
/*** 内存中添加登录账号(方式二)* AuthenticationManagerBuilder 允许配置认证账号*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN").and().withUser("user").password("123456").roles("USER").and().withUser("panjunbiao").password("123456").roles("USER");
}/*** 由于5.x版本之后默认启用了委派密码编译器,* 因而按照以往的方式设置内存密码将会读取异常,* 所以需要暂时将密码编码器设置为 NoOpPasswordEncoder*/
@Bean
public PasswordEncoder passwordEncoder()
{return NoOpPasswordEncoder.getInstance();
}
2.3 完整的示例代码
完整的 WebSecurityConfig 类(Spring Security 配置类)的代码。
package com.pjb.securitydemo.config;import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;/*** Spring Security 配置类* @author pan_junbiao**/
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{@Overrideprotected void configure(HttpSecurity http) throws Exception{http.authorizeRequests() //返回一个URL拦截注册器.antMatchers("/admin/api/**").hasRole("ADMIN") //设置授权角色.antMatchers("/user/api/**").hasRole("USER") //设置授权角色.antMatchers("/app/api/**","/captcha.jpg").permitAll() //公开其权限.anyRequest() //匹配所有的请求.authenticated() //所有匹配的URL都需要被认证才能访问.and() //结束当前标签,让上下文回到 HttpSecurity.formLogin() //启动表单认证.loginPage("/myLogin.html") //自定义登录页面.loginProcessingUrl("/auth/form") //指定处理登录请求路径.permitAll() //使登录页面不设限访问.and().csrf().disable(); //关闭CSRF的防御功能}/*** 内存中添加登录账号(方式一)*/@Beanpublic UserDetailsService userDetailsService(){InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withUsername("admin").password("123456").roles("ADMIN").build());manager.createUser(User.withUsername("user").password("123456").roles("USER").build());manager.createUser(User.withUsername("panjunbiao").password("123456").roles("USER").build());return manager;}/*** 内存中添加登录账号(方式二)* AuthenticationManagerBuilder 允许配置认证账号*//*@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception{auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN").and().withUser("user").password("123456").roles("USER").and().withUser("panjunbiao").password("123456").roles("USER");}*//*** 由于5.x版本之后默认启用了委派密码编译器,* 因而按照以往的方式设置内存密码将会读取异常,* 所以需要暂时将密码编码器设置为 NoOpPasswordEncoder*/@Beanpublic PasswordEncoder passwordEncoder(){return NoOpPasswordEncoder.getInstance();}
}