SpringSecurity 使用方法
一、本地视图方式校验(Thymeleaf)
1、基本用户名校验
-
配置Pom依赖
//父工程 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> </parent> //依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> </dependencies>
-
配置application.yaml
spring: application: name: springboot-security-test server: port: 8899
-
配置SpringBootApplication主启动
@SpringBootApplication public class SpingBootApplicationMain { public static void main(String[] args) { SpringApplication.run(SpingBootApplicationMain.class, args); } }
-
编写Controller
@RestController @RequestMapping("/sec") public class TestController { @GetMapping("hello") public String hello(){ return "认证成功"; } }
1.1、从配置文件中进行校验
修改配置文件中的application.yaml添加Security配置
security: user: name: byz password: 123
启动工程进行验证,访问配置的映射器路径:
进入登陆页面,输入在配置文件中的用户名密码进入
1.2、从配置类中进行校验
1、修改配置文件,注释掉:
# security:
# user:
# name: byz
# password: 123
2、新建一个配置类
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description TODO
* @Date 2022-04-14 09:43:31
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//注入密码加密编码器
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = (BCryptPasswordEncoder) passwordEncoder();
String password = passwordEncoder.encode("123");
auth.inMemoryAuthentication().withUser("byz").password(password).roles("admin");
}
}
1.3、从数据库或者其他方式自定义校验UserDetailService
-
自定义
USerDeatilService
实现UserdetailService
接口@Service("userDetailsService") //名称必须叫这个 public class MyUserDetailsServiceImpl implements UserDetailsService { //这里注入数据库查询逻辑,实际校验用户是否存在 //注入mapper进行校验 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { /* * 1、使用Mapper查询数据库 * 2、判断是否查询到用户 * (这里假定查询结果) * */ MyUser user = new MyUser(); List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user,ROLE_admin"); if (user != null) { /** * 从数据库查询到的用户 * 实质上需要返回一个数据库查询到的用户 * 并将其封装成为一个UserDetail对象进行返回,系统会自动校验其中的密码以及用户名信息 */ return new User("byz", new BCryptPasswordEncoder().encode("123"), grantedAuthorities); //这里USer为UserDetail的子类 } else { //查询不到就报错 throw new UsernameNotFoundException("用户名不存在!"); } } }
-
设置配置类并且重写配置权限方法
configure(AuthenticationManagerBuilder auth)
/** * @Author BaiYZ * @Program SpringSecurityDemo * @Description TODO * @Date 2022-04-14 09:43:31 */ @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { //注入密码编码器 @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //设置自定义的USerDetailService逻辑,以及配置密码编码器 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }
-
配置类中配置Httpl拦截方法
/** * 重写http加密校验规则 */ @Override protected void configure(HttpSecurity http) throws Exception { http //配置登陆也米娜 .formLogin() //自定义登陆页面 ,这里会调用UserNamePasswordFilter .loginPage("/user/login") //登陆访问url地址 由控制器处理 .loginProcessingUrl("/login") //登陆成功url地址 由控制器处理 .defaultSuccessUrl("/user/success") //配置路径权限 .and() .authorizeRequests() //表示配置地址权限访问 //配置以路径匹配来怕配置权限 .antMatchers("/", "/index", "/user/login").permitAll() //配置指定页面使用角色管理 .antMatchers("/role/admin").hasRole("admin") .antMatchers("/role/service").hasAnyRole("user,test") //指定权限管理 .antMatchers("/role/auth").hasAuthority("admin") .antMatchers("/role/test").hasAnyAuthority("test") //对请求进行配置 POST Get .... .anyRequest().authenticated() //所有请求都需要授权 //csrf跨站请求配置 .and() .csrf().disable() //配置403 自定义逻辑 //403自定义错误url .exceptionHandling().accessDeniedPage("/unauth"); }
2、记住我rememberMe
2.1、记住我(基于数据库)
-
注入数据源
-
安全配置类中配置(注入)
PersistentTokenRepository
@Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); //启动时创建表,创建一次,也可以手动创建(进入这个PersistentTokenRepository获取建表的sql) jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; }
-
配置类中
protected void configure(HttpSecurity http) throws Exception { http .and() .rememberMe().tokenRepository(persistentTokenRepository()) //开启RememberMe设置操作数据库对象 .tokenValiditySeconds(60) //设置过期时间 .userDetailsService(userDetailsService); //设置查询数据库的逻辑板 返回UserDetails //其他配置.... }
-
页面配置复选框
名称必须叫做remember-me 框架底层设置
<input type="checkbox" name="remember-me"/>
3、退出登陆(默认行为)
默认的退出登录URL为
/logout
需要自定义就实现LogoutHandler接口
二、分布式Token校验
2.1、编写UserDetail对象(UserDetailService查询会使用到)
-
编写基础用户对象(存储数据库中)
/** * @Author BaiYZ * @Program SpringSecurityDemo * @Description 实际的实体类 * @Date 2022-04-15 17:01:52 */ @Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { @Serial private static final long serialVersionUID = 2211L; private String userName; private String password; private String age; private String birthday; }
-
编写SecurityUser用于
SpringSecurity
中权限用户封装/** * @Author BaiYZ * @Program SpringSecurityDemo * @Description 用于鉴权的实体类 * @Date 2022-04-15 17:02:31 */ @Data @NoArgsConstructor @AllArgsConstructor public class SecurityUser implements UserDetails { /** * 当前用户 */ private transient User currentUserInfo; /** * 权限列表 */ private List<String> permissionValueList; public SecurityUser(User currentUserInfo) { if(currentUserInfo != null){ this.currentUserInfo = currentUserInfo; } } /** * 获取授权列表 GrantedAuthority * @return Collection<? extends GrantedAuthority> */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { Assert.notNull(this.permissionValueList, "Cannot pass a null GrantedAuthority collection"); //遍历设置权限 Set<GrantedAuthority> setAuthorities = new HashSet<>(); for (String grantedAuthority : this.permissionValueList) { //权限不为空 if(StringUtils.hasText(grantedAuthority)){ SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(grantedAuthority); setAuthorities.add(simpleGrantedAuthority); } } return setAuthorities; } @Override public String getPassword() { return null; } @Override public String getUsername() { return null; } @Override public boolean isAccountNonExpired() { return false; } @Override public boolean isAccountNonLocked() { return false; } @Override public boolean isCredentialsNonExpired() { return false; } @Override public boolean isEnabled() { return false; } }
2.2、编写Security
2.1.1、编写自定义密码加密规则PassWordEncoder
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description TODO
* @Date 2022-04-15 14:56:21
*/
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
public DefaultPasswordEncoder() {
}
public DefaultPasswordEncoder(int strength) {
}
/**
* 自定义加密方式 --->> 也可以使用md5加密,返回加密字符串即可
* @param rawPassword 原始密码由Securuty框架传入
* @return
*/
@Override
public String encode(CharSequence rawPassword) {
return bCryptPasswordEncoder.encode(rawPassword);
}
/**
* 比对密码比较的是输入后加密与存储的加密密码比对
* @param rawPassword 框架获取的用户提交的密码
* @param encodedPassword 存储的加密后的密码
* @return bool 真为通过
*/
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
//输入密码加密
String encodeInput = bCryptPasswordEncoder.encode(rawPassword);
return encodedPassword.equals(encodeInput);
}
}
2.1.2 Token工具类
- 生成Token(jwt生成—JsonWebToken)
- 根据用户名获取Token
- Token中获取用户名
- 删除Token
Jwt介绍:
JWT头
- alg:表示签名算法
- typ:令牌类型
{ "alg":"HS256", "typ":"JWT" }
有效载荷 PayLoad(Claims)
- iss:发行人
- exp:到期时间
- sub:主题
- aud:用户
- nbf:在此之前不可用
- iat:发布时间
- jti:jwt id 标识一个JWt
- 私有字段
{ "iss":"xxx", "exp":"222", "sub":"loginedUSer", "nbf":"xxx", "aud":"user", "iat":"xxx", "jti":"xxx", "自定义字段":"value" }
签名哈希
对上面两部分进行签名,使用指定算法生成前面哈希
例子:
HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
完整JWt字符串:
拼装结构:
JWTString=Base64(Header).Base64(Payload).签名哈希
2.1.2.1、导入JWt依赖
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
</parent>
<!--JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.1.2.2、编写TokenManager工具类
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description token 管理工具类
* @Date 2022-04-15 15:25:04
*/
@Component("tokenManager")
public class TokenManager {
/**
* token有效期 这里设置60min
*/
private long tokenExpiredTime = 60 * 60 * 1000;
/**
* token密钥
* <b>一般实际项目中需要实际生成</b>
*/
private String tokenSecrect = "111111";
/**
* 根据Token获取用户名信息
* setSubject(String sub); 设置jwt加密对象
* setExpiration(Date exp); 设置过期时间
* signWith(SignatureAlgorithm alg, String base64EncodedSecretKey); 设置签名算法,签名密钥
* CompressionCodecs.GZIP 进行压缩
*/
public String createToken(String userName){
String token = Jwts.builder()
.setSubject(userName)
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiredTime))
.signWith(SignatureAlgorithm.ES512,tokenSecrect)
.compressWith(CompressionCodecs.GZIP).compact();
return token;
}
//根据用户名获取Token
/**
* Jwts.parser():获取jwt转化器
* setSigningKey(String key):设置签名密钥
* parseClaimsJws(String claimsJws):转化jwt
* getBody():获取body
* @param token 传入token串
* @return 返回信息
*/
public String getUserInfoFromToken(String token){
String userInfo = Jwts.parser()
.setSigningKey(tokenSecrect)
.parseClaimsJws(token)
.getBody()
.getSubject();
return userInfo;
}
public void removeToken(String token){
}
}
2.1.3 退出登陆处理器(Token)
2.1.3.1、使用Redis存储Token
- 引入Pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- application.yaml中配置Redis
spring:
redis:
port: 6379
host: localhost
2.1.3.2、编写退出处理器
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description Token退出登陆逻辑
* @Date 2022-04-15 16:11:20
*/
@Component("tokenLogOutHandler")
public class TokenLogOutHandler implements LogoutHandler {
/**
* 注入Token工具类
*/
private TokenManager tokenManager;
/**
* redis模板类
*/
private RedisTemplate redisTemplate;
public TokenLogOutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
/**
* 退出登陆逻辑
* @param request 请求
* @param response 响应
* @param authentication 授权信息封装
*/
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
//1、从header拿到Token
String token = request.getHeader("token");
//2、存在Token
//2.1、移出Token(从Redis删除)
if(token!=null){
tokenManager.removeToken(token);
String userInfoFromToken = tokenManager.getUserInfoFromToken(token);
redisTemplate.delete(userInfoFromToken);
}
//写响应相关操作,返回一个状态xxx
}
}
退出逻辑配置
@Override protected void configure(HttpSecurity http) throws Exception { http .and() .logout() .logoutUrl("/logout") //默认为logout .logoutSuccessHandler(tokenLogOutHandler) //自定义退出成功处理器 .deleteCookies("JSESSIONID") //要删除的Cookies }
2.1.4 未授权统一处理AuthenticationEntryPoint
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 未授权统一处理
* @Date 2022-04-15 16:32:12
*/
@Component("unAuthenticationEntryPoint")
public class UnAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
//处理未授权逻辑
}
}
未授权统一权限配置:
@Override protected void configure(HttpSecurity http) throws Exception { http //没有权限处理器 .exceptionHandling().authenticationEntryPoint(unAuthenticationEntryPoint) }
2.3、编写过滤器
2.3.1、认证过滤器
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 认证过滤器
* @Date 2022-04-15 16:47:42
*/
@Component("tokenLoginFilter")
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
/**
* redis模板类
*/
private RedisTemplate redisTemplate;
/**
* token工具类
*/
private TokenManager tokenManager;
/**
* 权限管理工具类
*/
private AuthenticationManager authenticationManager;
public TokenLoginFilter(RedisTemplate redisTemplate, TokenManager tokenManager, AuthenticationManager authenticationManager) {
this.redisTemplate = redisTemplate;
this.tokenManager = tokenManager;
this.authenticationManager = authenticationManager;
//设置不止用post提交
this.setPostOnly(false);
}
/**
* 重写尝试认证的方法
* 父类会进行调用
*
* @param request 请求
* @param response 响应
* @return Authentication 返回认证封装
* @throws AuthenticationException 认证异常
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//获取表单提交数据
try {
//从请求中获取用户信息
User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
//交给SpringSecurity管理
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword(), new ArrayList<>()));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
/**
* AbstractAuthenticationProcessingFilter调用子类attemptAuthentication认证之后
* <b>成功调用</b>
*
* @param request 请求
* @param response 响应
* @param chain 过滤器链
* @param authResult 认证结果Authentication
* @throws IOException id异常
* @throws ServletException servlet异常
*/
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
//获取认证之后用户信息UserDetail
SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
//封装token(这里使用用户名生成)
String token = tokenManager.createToken(securityUser.getCurrentUserInfo().getUserName());
//将用户名以及权限列表放入Redis
redisTemplate.opsForValue().set(token, securityUser.getPermissionValueList());
//将Token 返回前端!!!!前端会进行封装放到请求头中
//返回相关页面信息
}
/**
* AbstractAuthenticationProcessingFilter调用子类attemptAuthentication认证之后
* <b>成功调用</b>
*
* @param request 请求
* @param response 响应
* @param failed 失败的认证异常
* @throws IOException id异常
* @throws ServletException servlet异常
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
//认证失败的逻辑 比如返回页面error json
}
}
配置认证过滤器:
@Override protected void configure(HttpSecurity http) throws Exception { //增加自定义认证过滤器 http.addFilter(tokenLoginFilter) }
2.3.2、编写授权过滤器
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 授权过滤器
* @Date 2022-04-15 16:47:15
*/
@Component("tokenAuthenticationFilter")
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//获取当前认证成功用户权限信息
UsernamePasswordAuthenticationToken authResult = getAuthentication(request);
//判断存在权限信息,放到权限上下文
if (authResult != null) {
SecurityContextHolder.getContext().setAuthentication(authResult);
}
//放行过滤器
chain.doFilter(request, response);
}
/**
* 从请求中获取解析Token生成UsernamePasswordAuthenticationToken
*
* @param request 请求
* @return UsernamePasswordAuthenticationToken
*/
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
//拿到token
String token = request.getHeader("token");
if (token != null) {
//从token 解析用户名
String username = tokenManager.getUserInfoFromToken(token);
//从redis中获取权限列表
List<String> permissionList = (List<String>) redisTemplate.opsForValue().get("username");
List<GrantedAuthority> collect = null;
if (permissionList != null) {
collect = permissionList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
return new UsernamePasswordAuthenticationToken(username, token, collect);
}
return null;
}
}
配置授权过滤器:
@Override protected void configure(HttpSecurity http) throws Exception { //增加自定义授权过滤器 http.addFilter(tokenAuthenticationFilter) }
2.4、编写UserDetailService
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 实际查询数据库获取UserDetail
* @Date 2022-04-15 18:54:03
*/
@Service("userDetailService") //名称必须叫这个,否则无法注入
public class UserDetailServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名查询数据库,获取Security需要的对象
//返回UserDetail对象
return new SecurityUser();
}
}
配置自定义UserDetailService:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //增加自定义授权过滤器 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); }
2.5、编写核心配置类
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description token 核心配置类
* @Date 2022-04-15 18:22:00
*/
@Configuration
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 授权失败(为授权)统一处理器
*/
private final UnAuthenticationEntryPoint unAuthenticationEntryPoint;
/**
* 退出处理器
*/
private final TokenLogOutHandler tokenLogOutHandler;
/**
* 认证过滤器
*/
private final TokenLoginFilter tokenLoginFilter;
/**
* 授权过滤器
*/
private final TokenAuthenticationFilter tokenAuthenticationFilter;
/**
* 实际查询数据库获取UserDetail的逻辑
*/
private final UserDetailsService userDetailsService;
@Autowired
public TokenWebSecurityConfig(UnAuthenticationEntryPoint unAuthenticationEntryPoint, TokenLogOutHandler tokenLogOutHandler, TokenLoginFilter tokenLoginFilter,
TokenAuthenticationFilter tokenAuthenticationFilter,UserDetailsService userDetailsService) {
this.unAuthenticationEntryPoint = unAuthenticationEntryPoint;
this.tokenLogOutHandler = tokenLogOutHandler;
this.tokenLoginFilter = tokenLoginFilter;
this.tokenAuthenticationFilter = tokenAuthenticationFilter;
this.userDetailsService = userDetailsService;
}
/**
* 整体配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//没有权限处理器
.exceptionHandling().authenticationEntryPoint(unAuthenticationEntryPoint)
//自定义退出成功处理器
.and()
.logout()
.logoutUrl("/logout") //默认为logout
.addLogoutHandler(tokenLogOutHandler)
.deleteCookies("JSESSIONID") //要删除的Cookies
//配置所有的需要授
.and()
.authorizeRequests()
//这里增加需要hasAuth权限或者需要角色的地址认证
//配置以路径匹配来怕配置权限
// .antMatchers("/", "/index", "/user/login").permitAll()
//
// //配置指定页面使用角色管理
// .antMatchers("/role/admin").hasRole("admin")
// .antMatchers("/role/service").hasAnyRole("user,test")
.anyRequest().authenticated()
//关闭csrf跨域攻击
.and().csrf().disable()
//增加自定义认证过滤器
.addFilter(tokenLoginFilter)
//增加权限过滤器
.addFilter(tokenAuthenticationFilter);
}
/**
* 配置权限验证相关
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder());
}
/**
* 设置访问路径相关的
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
//设置不进行权限校验的路径
web.ignoring().antMatchers("/");
}
}
三、Handler配置
3.1、授权成功处理器
1、编写处理器
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 登陆成功处理逻辑
* @Date 2022-04-15 15:52:27
*/
@Component("authenticationSuccessHandlerImpl")
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//登陆成功的逻辑
}
}
2、配置到Security配置文件中
@Override
protected void configure(HttpSecurity http) throws Exception {
http.successHandler(authenticationSuccessHandlerImpl);
}
3.2、授权失败处理器
1、编写处理器
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 授权失败处理器
* @Date 2022-04-15 15:57:03
*/
@Component("authenticationFailureHandlerImpl")
public class AuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//授权失败的逻辑
}
}
2、配置到Security配置文件中
@Override
protected void configure(HttpSecurity http) throws Exception {
http.failureHandler(authenticationFailureHandlerImpl);
}
3.3、退出登陆处理器
1、编写处理器
/**
* @Author BaiYZ
* @Program SpringSecurityDemo
* @Description 退出登陆处理器
* @Date 2022-04-15 16:02:26
*/
@Component("logoutSuccessHandlerImpl")
public class LogoutSuccessHandlerImpl implements LogoutHandler {
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
//退出登陆的逻辑
}
}
2、配置到Sevurity配置文件中
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.and()
.logout()
.logoutUrl("/logout") //默认为logout
.logoutSuccessHandler(logoutSuccessHandlerImpl) //自定义退出成功处理器
.deleteCookies("JSESSIONID") //要删除的Cookies
}
四、权限注解
4.1、开启权限注解
@Configuration
/**
* 开启全局方法权限注解
* securedEnabled:开启安全注解
* prePostEnabled:前置后置过滤注解
*/
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
4.2、常用注解使用
4.2.1、@Secured
适用于方法上,标识当前这个方法是什么样的角色可以访问
/**
* securedEnable测试 开启角色权限
*
* @return
*/
@ResponseBody
@GetMapping("/securedEnabled")
@Secured({"ROLE_test"}) //允许test角色访问
public String securedEnabledTest() {
return "允许访问,这里是 @EnableGlobalMethodSecurity(securedEnabled = true) 注解配置使用@Secure";
}
4.2.2、@PreAuthorize
适用于方法上,可以用权限或者角色,先校验权限,后执行方法 不通过403
/**
* prePostEnabled测试 可以只用权限或者角色,先校验权限,后执行方法 不通过403
*
* @return
*/
@ResponseBody
@GetMapping("/prePostEnabled")
@PreAuthorize("hasAnyAuthority('admin')")
public String prePostEnabled() {
return "允许访问,这里是 @EnableGlobalMethodSecurity(prePostEnabled = true) 注解配置使用 提前校验@PreAuthorize";
}
4.2.3、@PostAuthorize
适用于方法上,有任何权限,先执行方法 后校验权限,不通过403
/**
* prePostEnabled测试 有任何权限,先执行方法 后校验权限
*
* @return
*/
@ResponseBody
@GetMapping("/postEnabled")
@PostAuthorize("hasAnyAuthority('test')")
public String postAuthorize() {
return "允许访问,这里是 @EnableGlobalMethodSecurity(prePostEnabled = true) 注解配置使用 后置校验@PostAuthorize";
}
4.2.4、@PostFilter
适用于方法上,输出匹配的指定值(留下这个其他过滤)
/**
* 权限校验之后,输出匹配的指定值(留下这个其他过滤)
*
* @return
*/
@ResponseBody
@RequestMapping("/postFilter")
@PostFilter("filterObject == 'admin'")
public List<String> getAllListPostFilter() {
ArrayList<String> list = new ArrayList<>();
list.add("admin");
list.add("test");
return list;
}
会输出 admin , test被过滤掉
4.2.4、@PreFilter
适用于方法上,权限校验之后,输出匹配的指定值
/**
* 权限校验之后,输出匹配的指定值
*
* @return
*/
@ResponseBody
@RequestMapping("/preFilter")
@PreFilter("filterObject == 'test'")
public List<String> getAllListPreFilter(@RequestBody List<String> input) {
return input;
}
输入:
[
"test","admin","user","abcd"
]
输出:
[
"test"
]
五、坑
-
角色配置中不需要增加“ROLE_”前缀,但是注解中需要增加,数据库中不用增加
public ExpressionInterceptUrlRegistry hasRole(String role) { return access(ExpressionUrlAuthorizationConfigurer //这里会传入前缀 .hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role)); }
this.rolePrefix
在构造方法中进行设置,如果没有覆盖默认的设置GrantedAuthorityDefaults.class
会使用前缀