然后我们创建一个继承WebSecurityConfigurerAdapter的配置类,定义权限访问策略,同时再添加一个路径为“/hello”的接口,根据代码注释我们可以看出,访问项目主路径可以不需要验证,访问其余路径则需要验证。启动项目,访问localhost:8080可以直接通过,但访问localhost:8080\hello则会自动跳转到localhost:8080/login路径要求登录。这样说明Spring Security的安全策略已经生效了,Spring Boot与Spring Security的环境搭建也完成了。
@Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { /** * 拦截策略 * 定义哪些路径需要被拦截,哪些路径不需要拦截 */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("http://www.likecs.com/").permitAll() //项目主路径可以放行 .anyRequest().authenticated() //其余所有请求需要验证 .and().logout().permitAll() //允许登出可以访问 .and().formLogin(); //允许表单登录 http.csrf().disable(); //关闭csrf认证 } @Override public void configure(WebSecurity web) throws Exception { /** * 忽略静态资源的拦截 */ web.ignoring().antMatchers("/js/**", "/css/**"); } } 2.2 常用Case实现 2.2.1 只要能登录即可 只要登录就可以访问项目所有资源路径,也不用写单独的登录页面,这里就会用到Spring Security提供的基于内存的验证。在SpringSecurityConfig类中继续重写configure(AuthenticationManagerBuilder auth)这个方法。Spring security 5.0之后新增了多种加密方式,改变了默认的密码格式,新的密码存储格式是“{id}…………”.前面的id是加密方式,id可以是bcrypt、sha256等,后面跟着的是加密后的密码。也就是说,程序拿到传过来的密码的时候,会首先查找被“{”和“}”包括起来的id,来确定后面的密码是被怎么样加密的,如果找不到就认为id是null。这时候程序会报错:There is no PasswordEncoder mapped for the id “null”.实际应用中也可以自定义加密方式,只需要继承PasswordEncoder接口即可。
@Configuration @EnableWebSecurity public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //创建一个用户名为admin,密码为123456,角色为ADMIN的用户 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("admin") .password(new BCryptPasswordEncoder().encode("123456")) .roles("ADMIN"); //可指定多个用户 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("zhangsan") .password(new BCryptPasswordEncoder().encode("123456")) .roles("DEMO"); } } 2.2.2 有指定的角色,每个角色有指定的权限 添加一个限定角色的请求,需要有ADMIN角色的才能访问,“ROLE_”为RoleVoter中定义的前缀,在前面自定义决策中提到过。同时,这里还需要注意的是,使用@PreAuthorize这个注解时,一定要在类上加上@EnableGlobalMethodSecurity(prePostEnabled = true)注解@PreAuthorize才会生效。这样admin用户就可以访问/roleAuth,但zhangsan则不可以访问/roleAuth。
@SpringBootApplication @RestController @EnableGlobalMethodSecurity(prePostEnabled = true) public class DemoApplication { /**中间代码省略**/ @PreAuthorize("hasRole(\'ROLE_ADMIN\')") @RequestMapping("/roleAuth") public String role() { return "admin auth"; } } 实际场景中用户角色一般是存储在数据库中的,前面提到过Spring Security的数据库管理需要实现UserDetailsService接口,定义数据库相关查询,返回UserDetails对象。
package com.mall.demo; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; @Component public class MyUserService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return null; } } @Autowired private MyUserService myUserService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserService); /** * Spring Security提供的默认数据库验证 */ auth.jdbcAuthentication() .usersByUsernameQuery("") //查询users .authoritiesByUsernameQuery(""); //查询权限 } 数据库管理在实际项目能更好的说明,这里我们回到Spring Security权限配置,之前使用过@PreAuthorize这个注解来控制方法是否能被调用,实际上Spring Security提供了4个这样的注解,分别是@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter,@PreAuthorize和@PostAuthorize的作用分别是在方法调用前和调用后对权限进行检查,@PreFilter和@PostFilter的作用是对集合类的参数或返回值进行过滤。
//传入的id参数小于10 //传入的username=当前用户名 //传入的User对象的用户名=zhangsan @PreAuthorize("#id<10 and principal.username.equals(#username) and #user.username.equals(\'zhangsan\')") //验证返回结果是否是偶数 @PostAuthorize("returnObject%2==0") @RequestMapping("/test1") public Integer test1(Integer id, String username, User user) { return id; } //过滤传入的参数保留偶数 @PreFilter("filterObject%2==0") //过滤返回结果保留被4整除的数 @PostFilter("filterObject%4==0") @RequestMapping("/test2") public List<Integer> test2(List<Integer> idList) { return idList; }