目录
1.导入依赖
2.继承WebSecurityConfigurerAdapter
3.实现UserDetailsService
4.记住我
5.用户注销
6.CSRF理解
7.注解功能
7.1@Secured
7.2@PreAuthorized
7.3@PostAuthorized
7.4@PostFilter
7.5Z@PreFilter
8.原理解析
1.导入依赖
首先,在pom.xml文件中添加Spring Security依赖:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
</dependencies>
2.继承WebSecurityConfigurerAdapter
创建一个WebSecurityConfig类,继承WebSecurityConfigurerAdapter:
继承WebSecurityConfigurerAdapter是因为我们要重写里面的方法 定义一些安全配置 例如:注销
功能、访问控制、登录页面等。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {}
protected void configure(HttpSecurity http)这个方法用于配置拦截保护的HTTP请求 配置HTTP安
全性 定义哪些URL应该受到保护 哪些用户可以访问哪些URL 以及保护的URL应该执行哪些安全措
施
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//.loginPage("/login.html")//指定登录页面(这里不会前端,所以不写页面了)//.loginProcessingUrl("/authentication/form")//登录访问路径(登录页面的form表单提交路径)//.failureUrl("/error.html")//用户名或密码错误访问的页面//.usernameParameter("username")//.passwordParameter("password") //要认证的 用户名, 密码 的参数名,默认username, password.defaultSuccessUrl("/index/toIndex").permitAll()//defaultSuccessUrl:登录成功后,是否始终跳转到登录成功url,它默认为false。 permitAll()就是指不需要拦截.and().authorizeRequests().antMatchers("/index/testRoleAndPermission").hasAnyRole("admin")//设置哪些路径需要什么权限.antMatchers("/index/anyOne","/index/anyTwo").permitAll()//设置哪些路径可以直接访问,不需要认证.anyRequest().authenticated();//其余任何请求都需要认证.and().csrf().disable();//关闭csrf防护}
public void configure(AuthenticationManagerBuilder auth) 这个方法主要是配置 身份验证机制 就
是配置密码匹配器和用户从数据库查询用户的service
// 用于配置身份验证机制// AuthenticationManagerBuilder常用方法:(下面的方法我没有写参数,实际情况可能是有参的)// inMemoryAuthentication(): 这个方法允许您在内存中配置用户详细信息。您可以指定用户名、密码和用户角色。这对于快速测试和开发目的非常方便。// userDetailsService(): 这个方法接受一个UserDetailsService对象,用于加载用户的详细信息。UserDetailsService是一个接口,您需要实现它来根据用户名加载用户信息。// jdbcAuthentication(): 这个方法允许您使用JDBC来加载用户详细信息。您需要提供一个DataSource对象和相应的查询语句来检索用户名、密码和角色信息。// ldapAuthentication(): 这个方法用于配置LDAP(轻量级目录访问协议)身份验证。您需要提供LDAP服务器的连接信息和相应的查询语句。// authenticationProvider(): 这个方法允许您提供自定义的AuthenticationProvider实现,用于验证用户的身份。// userDetailsService(T userDetailsService): 根据传入的自定义UserDetailsService做身份验证。// 返回值: 会返回一个DaoAuthenticationConfigurer(该类继承AbstractDaoAuthenticationConfigurer抽象类)}// passwordEncoder(PasswordEncoder passwordEncoder): 来自于抽象类, 用于指定自定义密码匹配器,便于后续springsecutiry底层后续使用。@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}
public PasswordEncoder passwordEncoder() 这个是密码匹配器 SpringSecurity校验密码的时候
就要用到密码匹配器 对密码进行加密解密校验对比操作
// 必须要个这种密码匹配器,springsecurity要求的,必须对密码进行加密。@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}
3.实现UserDetailsService
客户端输入用户名和密码 后端必须要从数据库查询出数据 进行校验 需要实现UserDetailsService
重写查询校验方法
/*** @author lihao* @version 1.0* @ClassName SecurityUserServiceImpl* @date 2023/5/25 9:43* @apiNote UserDetailsService:用于加载用户的详细信息,以供身份验证和授权使用。它提供了一个方法loadUserByUsername(String username)用于用户名获取用户的详细信息。* 我们想更改springcurity的认证,我们就可以实现UserDetailsService,因为security底层就是用其做核心认证的。**/
@Service
public class SecurityUserServiceImpl extends ServiceImpl<SecurityUserMapper, SecurityUser> implements SecurityUserService, UserDetailsService {@Resourceprivate SecurityUserMapper securityUserMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {LambdaQueryWrapper<SecurityUser> wrapper = new LambdaQueryWrapper<>();wrapper.eq(SecurityUser::getUsername,username);SecurityUser user = securityUserMapper.selectOne(wrapper);if (user==null) {throw new UsernameNotFoundException("用户没找到");}// loadUserByUsername方法需要返回一个UserDetails(接口),常用实现类User。因此我们返回一个SpringSecurity提供的User对象。// 该User对象所需的参数中,密码必须加密(由springsecurity要求),因为我前面已经在SecurityConfig配置了加密器,所以这里就不需要对密码进行加密。// 否则需要加密:new BCryptPasswordEncoder().encode(user.getPassword()) ---- 可以这么写// 第三个参数,他需要一个权限集合,这里只做认证,所以随便搞一个角色集合,不影响认证,注意,这里第三个参数不能直接传null,必须给它一个集合。List<GrantedAuthority> adminAuthority = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");return new User(user.getUsername(), user.getPassword(), adminAuthority); // 已在SecurityConfig配置了加密组件,所以不需要对这里的密码加密。}
4.记住我
客户端登录一次以后 下次访问不用登录 直接访问
需要在configure(HttpSecurity http)配置
// 开启记住我功能
http.rememberMe().tokenRepository(tokenRepository).userDetailsService(usersService);
.tokenRepository(tokenRepository)
:这是为"记住我"功能配置token存储库。token是用来存储
和验证用户会话信息的。这通常是一个在数据库或其他持久性存储中保存信息的对象。
.userDetailsService(usersService)
:这是为"记住我"功能配置用户详情服务。
UserDetailsService
是Spring Security中的一个接口,它有一个方法loadUserByUsername
,用于根
据用户名获取用户信息。usersService
需要实现这个接口,并能够根据用户名找到用户的详细信
息。这对于"记住我"功能很重要,因为它需要知道用户的详细信息(例如他们的加密密码)以验证
他们的身份。
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl jdbcTokenRepository = new
JdbcTokenRepositoryImpl();
// 赋值数据源
jdbcTokenRepository.setDataSource(dataSource);
// 自动创建表,第一次执行会创建,以后要执行就要删除掉!
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;}
}
还可以设置有效期
5.用户注销
http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll
();
http.logout()
: 这是调用Spring Security的HTTP安全性配置的logout方法,它配置了用户的注销功能。.logoutUrl("/logout")
: 这告诉Spring Security,当用户点击注销时,应该将他们重定向到URL "/logout"。这通常是应用程序的一个特殊页面,它执行注销操作并终止用户的会话。.logoutSuccessUrl("/index")
: 当注销操作成功后,用户将被重定向到这个URL。在这个例子中,用户将被重定向到应用程序的"/index"页面。.permitAll()
: 这告诉Spring Security,所有用户都应该能够访问注销功能。换句话说,它不限制谁可以注销,所有用户都可以。
总的来说,这段代码的目的是配置Spring Security的注销功能,使得所有用户都可以注销,并且当
他们注销成功后,他们将被重定向到应用程序的"/index"页面。
6.CSRF理解
7.注解功能
使用注解功能前 要先开启注解功能 在启动类加上@EnableGlobalMethodSecurity()注解 注解里面
要使用什么注解 就在括号里填写 xxx注解=true 例如:
@EnableGlobalMethodService(securedEnabled = true) 使用@securedEnabled注解
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled=true)
public class DemosecurityApplication {
public static void main(String[] args) {SpringApplication.run(DemosecurityApplication.class, args);}
}
7.1@Secured
判断是否具有这个角色 字符串前缀需要添加ROLE_
// 测试注解:
@RequestMapping("testSecured")
@ResponseBody
@Secured({"ROLE_normal","ROLE_admin"})
public String helloUser() {
return "hello,user";
}
7.2@PreAuthorized
@RequestMapping("/preAuthorize")
@ResponseBody
//@PreAuthorize("hasRole('ROLE_管理员')")
@PreAuthorize("hasAnyAuthority('menu:system')")
public String preAuthorize(){System.out.println("preAuthorize");
return "preAuthorize";
}
7.3@PostAuthorized
@RequestMapping("/testPostAuthorize")
@ResponseBody
@PostAuthorize("hasAnyAuthority('menu:system')")
public String preAuthorize(){System.out.println("test--PostAuthorize");
return "PostAuthorize";
}
7.4@PostFilter
@RequestMapping("getAll")
@PreAuthorize("hasRole('ROLE_管理员')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){ArrayList<UserInfo> list = new ArrayList<>();list.add(new UserInfo(1l,"admin1","6666"));list.add(new UserInfo(2l,"admin2","888"));
return list;
}
7.5Z@PreFilter
@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_管理员')")
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo>
list){list.forEach(t-> {System.out.println(t.getId()+"\t"+t.getUsername());});
return list;
}
8.原理解析
SpringSecurity本质:其实就是一个过滤器链,内部提供了各种功能的过滤器。springsecurity底层就是这些过滤器一层一层的执行帮我们实现的权限管理。
图中只展示了核心过滤器,其它的非核心过滤器并没有在图中展示。
UsernamePasswordAuthenticationFilter: 用于处理基于表单的登录请求,从表单中获取用户名和
密码。 默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name
值为 username 和 password。 这两个值可以通过设置这个过滤器的usernameParameter 和
passwordParameter 两个参数的值进行修改。 其内部还有 登录成功 或 失败后 进行处理的
AuthenticationSuccessHandler 和 AuthenticationFailureHandler,这些都可以根据需求做相关改
变。
ExceptionTranslationFilter: 处理 AccessDeniedException 和 AuthenticationException 异常。 3.
FilterSecurityInterceptor: 是用于保护web资源的,使用 AccessDecisionManager 对当前用户进
行授权访问。
SpringSecurity认证流程: