Authority 权限
Credential 证书
Grant 授予
Authentication 身份验证
一、添加spring-boot-start-security依赖即可实现默认的username+password登录。(默认用户认证)
<!-- 依赖:spring-security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
启动web应用,访问站点资源时,会出现spring-security提供的默认登录页面
其默认用户名为:user
登录密码在启动信息中可以找到:
填写正确即可登录成功。
这个最简单的配置适用于只有一个用户且每次系统启动后查阅更新密码的系统。当然,这种系统不常见。
二、实现自定义的固定用户和密码(内存用户认证)
需添加自定义配置,我们以java配置方式实现。
创建一个继承自org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter的类;
类上以@Configuration和@EnableWebSecurity注解,表明这是一个java配置类,并启用spring-security身份认证;
覆写configure(AuthenticationManagerBuilder auth)方法;
调用auth对象的.inMemoryAuthentication().withUser("xxx").password("xxx").roles("USER")等方法,指定用户、密码及角色
多个用户可以调用.and()方法来连接.withUser方法
@Configuration @EnableWebSecurity public class SecurityConfigA extends WebSecurityConfigurerAdapter{ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() // 内存用户认证 .withUser("xxx").password("xxx").roles("USER") // 配置用户xxx密码xxx及角色sys .and() .withUser("yyy").password("yyy").roles("USER") // 配置用户yyy密码yyy及角色sys ; } }
重启web应用后,默认的user用户登录方式已失效,
现在可以用户xxx或yyy登录(针对spring4版本)
使用spring5版本的话,在此,会报错:java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null",登录页面无法跳转
spring5要求必须指定编译码器,我们可以用BCryptPasswordEncoder。
修改一下configure(AuthenticationManagerBuilder auth)方法,填加一行代码:.passwordEncoder(new BCryptPasswordEncoder()) // 指定加密方式
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() // 内存用户认证 .passwordEncoder(new BCryptPasswordEncoder()) // 指定加密方式 .withUser("xxx").password("xxx").roles("USER") // 配置用户xxx密码xxx及角色sys .and() .withUser("yyy").password("yyy").roles("USER") // 配置用户yyy密码yyy及角色sys ; }
重新登录,还出错:
控制台有提示:Encoded password does not look like BCrypt(看上去不像BCrypt编码的密码)
我们的密码"xxx"是以明文方式传递的,用new BCryptPasswordEncoder().encode("xxx")改为密文即可。
@Configuration @EnableWebSecurity public class SecurityConfigA extends WebSecurityConfigurerAdapter{ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() // 内存用户认证 .passwordEncoder(new BCryptPasswordEncoder()) // 指定加密方式 .withUser("xxx").password(new BCryptPasswordEncoder().encode("xxx")).roles("USER") // 配置用户xxx密码xxx及角色sys .and() .withUser("yyy").password(new BCryptPasswordEncoder().encode("yyy")).roles("USER") // 配置用户yyy密码yyy及角色sys ; } }
以上是修改后的配置类,再次重启登录就正常了。
这个简单的配置适用于拥有少数明确固定用户且密码不得改变的系统。当然,这种系统不灵活。
三、实现自定义用户及密码登录的系统(UserDetailsService认证)
依然使用上面的配置类;
只是调用auth的.userDetailsService方法,该方法需要一个UserDetailsService接口作为参数;
需要实现UserDetailsService接口的loadUserByUsername(String username):UserDetails方法来完成用户身份认证;
loadUserByUsername(String username)返回一个UserDetails接口;
UserDetails接口用户名、密码、角色等信息;
注意指定密码编译器,可参考前例。
我们用一个私有方法来提供UserDetails接口作为示例,实际运用时推荐调用一个实体服务方法(例如:UserService.findBy(String username):User)来提供UserDetails接口。代码如下:
@Configuration @EnableWebSecurity public class SecurityConfigB extends WebSecurityConfigurerAdapter{ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(new UserDetailsService(){ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return findBy(username); //findBy(username)仅是一个示例方法 //return UserService.findByName(username); //通常,应该用一个Serice来实现 } // 示例方法findBy(username) private UserDetails findBy(String username) { return new UserDetails(){ private String username = "aaa"; //假定用户名为aaa private String password = "aaa"; //假定用户密码为aaa @Override public String getUsername() { return username; } @Override public String getPassword() { // 注意在此返回指定密码编译器编译的密文密码 return new BCryptPasswordEncoder().encode(password); } //以上属性通常是自定义实体类User定义的 //以下属性是User实现UserDetails接口必须的 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return java.util.Arrays.asList(new SimpleGrantedAuthority("USER")); } @Override public boolean isAccountNonExpired() { return true; //默认账户未过期 } @Override public boolean isAccountNonLocked() { return true; //默认用户未被锁定 } @Override public boolean isCredentialsNonExpired() { return true; //默认证书未过期 } @Override public boolean isEnabled() { return true; //默认有效,即用户未被停用 }}; }}) .passwordEncoder(new BCryptPasswordEncoder()) // 指定密码编译器 ; } }
配置类经过以上修改,再次重启就以用户aaa密码aaa正常登录了。
这个简单的配置适用于拥有少数明确固定用户且密码不得改变的系统。当然,这种系统不灵活。
需注意事项:
Arrays来自java.util.Arrays;
spring5中必须指定密码编译器,.passwordEncoder(new BCryptPasswordEncoder())
new SimpleGrantedAuthority("USER")只是示例性的简单授权,实际应用中应以数据源来提供用户角色。
当我们以MyUserDetailsService implements UserDetailsService后,代码主体是:
@Configuration @EnableWebSecurity public class SecurityConfigB extends WebSecurityConfigurerAdapter{ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth
.userDetailsService(new MyUserDetailsService())
.passwordEncoder(new BCryptPasswordEncoder()) // 指定密码编译器 ; } }
二、配置自定义页面
控制器,/loginView,指向
自定义的登录页面loginView.html,action="/loginView"
自定义SecurityConfig实现WebSecurityConfigurerAdapter
SecurityConfig extends WebSecurityConfigurerAdapter
加两个注解
@Configuration
@EnableWebSecurity
@Configuration //java 配置
@EnableWebSecurity //
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/loginView") // 指定登录页面
.and()
.authorizeRequests().anyRequest().authenticated() // 所有资源请求都需权限验证
.and()
.csrf().disable(); // 禁用csrf
}
}
有了自己的登录页面
配置内存用户认证
重写
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication() // 内存用户认证
.withUser("sys").password("sys").roles("USER"); // 配置系统默认用户sys
}
java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"
必须有密码编码
auth.inMemoryAuthentication().withUser("sys").password("sys")