官方文档:Hello Spring Security :: Spring Security
目录
一、认证流程
二、自定义认证实现
2.1实现UserDetailsService接口
2.2 配置自定义登录页面
先了解下SpringSecurity认证的流程,如下图(图片来自官网):
step1:用户提交请求,AbstractAuthenticationProcessingFilter会从请求信息中生成Authentication对象,Authentication对象根据 AbstractAuthenticationProcessingFilter子类决定;
step2:通过AuthenticationManager对Authentication进行认证;
step3:登录成功执行4,登录失败执行3;
web服务,用户登录基本都是通过form表单提交,所以重点看form认证的实现。
认证流程和上图一样,过滤器是UsernamePasswordAuthenticationFilter,继承AbstractAuthenticationProcessingFilter
通过扩展UserDetailsService#loadUserByUsername接口来获取数据库中保存的用户信息,security将接口返回的正确用户信息与用户提交的信息进行认证,来实现登录校验。
@Service
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// todo 根据用户名进行数据库查询,如果查不到对应账号,则抛出UsernameNotFoundException
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String password = encoder.encode("1234");
List authorities = AuthorityUtils.createAuthorityList("admin");
return User.withUsername("janice").password(password).authorities(authorities).build();
}
}
说明:即使接口没有设置访问的角色和资源权限,authorities也必须要设置,否则登录会报错,如图:
启动服务验证,登录成功
项目中往往都是需要有自己的登录页面,这时就需要进行一些配置来实现。
先在static下加一个简单的页面,用于测试:
注意(可查看源码UsernamePasswordAuthenticationFilter#attemptAuthentication):
1、input中的name,必须和源码中的用户名和密码命名一下,否则会取不到提交的数据;
2、请求必须是POST请求,否则不会进行处理;
重写protected void configure(HttpSecurity http),配置自定义登录页面
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailService myUserDetailService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html") // 登录页面
.loginProcessingUrl("/user/login") // 登录接口,与form表单提交链接对应
.defaultSuccessUrl("/index.html").permitAll() // 登录成功后跳转链接
.and().authorizeRequests()
.antMatchers("/user/login").permitAll() // 设置免登录链接
.anyRequest().authenticated()
.and().csrf().disable(); // 关闭csrf防护
}
}
注意:csrf需先关闭,否则会对POST、PUT、DELETE请求进行csrf验证,登录请求不会通过。
启动服务进行测试
跳转到了自定页的登录页面
方法有:hasAuthority、hasAnyAuthority、hasRole、hasAnyRole
用户信息中加上数据库中查到的用户拥有的角色和资源权限
1、User对象中有设置资源和角色两种接口,但不能同时使用.authorities(authorities).roles(""),因为roles接口也是用authorities实现,最后的配置会覆盖前面的配置,导致用户权限缺失;
2、authorities中设置角色权限,需要加上前缀:ROLE_;
@Secured({"ROLE_tester"})
@PreAuthorize("hasAnyAuthority('admin')")
开启注解:@EnableGlobalMethodSecurity(prePostEnabled = true)
启动服务,测试
对于无访问权限的接口,需要配置跳转自定义无权限访问页面,只需要加上
http.exceptionHandling().accessDeniedPage("/unauth.html");
加一个简单的html页面,启动服务测试: