在说完了Spring Security框架的功能和执行流程后,就到了写它Spring Boot的集成,先来看最简的配置:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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>
此时,直接运行程序,页面会跳出都会弹出一个“需要授权”的验证框:
Spring Security会默认生成一个用户,用户名为user,密码可以再IDE的控制台(Console)中查看,输入用户名和密码后就可以访问请求的URL了。
在我们实际的开发中,肯定需要我们定义访问哪些URL需要登录并拥有相关的权限,访问哪些URL不需要登录,以及验证失败后的跳转页面等。配置方式:定义继承WebSecurityConfigurerAdapter 类WebSecurityConfig类,并加上@Configuration 和@EnableWebSecurity两个注解,开启Spring Security功能,并交由容器进行管理。具体配置如下:
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ @Override protected void configure(HttpSecurity http) throws Exception { // TODO Auto-generated method stub http .authorizeRequests() //指定任何用户都可以访问‘/‘路径,permitAll()表示任何用户都可以访问 .antMatchers("/").permitAll() //以"/admin/"开头的URL,只能让"USER"角色权限的用户访问 .antMatchers("/user/**").hasRole("USER") .and() //通过formLogin方法登录,并设置登录url为/login,登录成功后跳转/user页面 .formLogin() .loginPage("/login").defaultSuccessUrl("/user") .and() .logout() //指定登出的url,登出成功后跳转的url路径 .logoutUrl("/logout").logoutSuccessUrl("/login"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ /** * 在内存中创建一个名为 "user" 的用户,密码为 "pwd",拥有 "USER" 权限,密码使用BCryptPasswordEncoder加密 */ auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER"); }
/**
* 添加 UserDetailsService, 实现自定义登录校验
*/
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception{
builder.userDetailsService(anyUserDetailsService);
}
}
这是Spring Security进行权限校验管理的一个基本配置啦!
其实看到这里心里还是有很多疑惑:
- 一般用户的信息都是保存在数据库中,那就还要涉及从数据库中读取信息进行验证;
- 登录成功后登录信息保存在哪,保存在SecurityContextHolder中吗?;
- 一般来说还需要自定义用户认证的流程,因为你要从数据库中读取数据,发生错误时需要相关的提示信息;
- 用户登录具体的验证流程是什么样的,该如何重写自定义验证流程?;
这些疑惑网上早就有人想到了,接着看下面的文章。
在知乎和简书上面找到了两张认证流程的图,直观地展示一下:
图一
图二
在Spring Security入门(二)中提到,用户登录时登录信息会封装在Authentication中,然后传递给AuthenticationManager实例进行验证。Authentication是如何在AuthenticationManager实例中进行验证的?具体的流程就如图一所示:
- AuthenticationManager,它是验证管理类的总接口;
- 而具体的验证管理需要ProviderManager类,它具有一个List<AuthenticationProvider> providers属性,这实际上是一个AuthenticationProvider实例构成的验证链;
- 链上都是各种AuthenticationProvider实例,这些实例进行具体的验证工作。
所以根据图二,大致的验证流程如下:
- 后端从前端的表单得到用户密码,包装成一个Authentication类的对象;
- 将Authentication对象传给“验证管理器”ProviderManager进行验证;
- ProviderManager在一条链上依次调用AuthenticationProvider进行验证;
- 其中DaoAuthenticationProvider依赖于UserDetailsService,调用UserDetailsService实例的loadUserByUsername方法,会返回一个UserDetails实例,将UserDetails实例与Authentication对象进行比对;
- 验证成功则返回一个封装了权限信息的Authentication对象(即对象的Collection<? extends GrantedAuthority>属性被赋值);
- 将此对象放入安全上下文SecurityContext中;
- 需要时,可以将Authentication对象从SecurityContextHolder上下文中取出。
还记得上文提到说,用户数据存放于数据库时该如何读取用户信息进行认证吗?没错,就是自定义一个UserDetailsService的类,主要是重写它的loadUserByUsername方法,将用户信息封装在UserDetails对象中。看个具体的栗子:
@Service public class AnyUserDetailService implements UserDetailsService{ //DAO层的代码省略 @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // TODO Auto-generated method stub User user = userMapper.selectByUsername(username); if(user == null) { throw new UsernameNotFoundException("用户不存在!"); } List<SimpleGrantedAuthority> simpleGrantedAuthorities = createAuthorities(user.getRoles()); return new User(userEntity.getUsername(), userEntity.getPassword(), simpleGrantedAuthorities); }
自定义了类之后还要配置才能生效,还是在WebSecurityConfig类中进行配置,参见上文。
原文地址:https://www.cnblogs.com/linyukun/p/9862730.html