SpringSecurity学习笔记


SpringSecurity学习笔记

1)什么是SpringSecurity:

Spring Security是一个功能强大且高度可定制的,主要负责为Java程序提供声明式的 身份验证和访问控制 的安全框架。

Spring Security的底层主要是 基于 Spring AOP 和 Servlet 过滤器 来实现安全控制,它提供了全面的安全解决方案,同时授权粒度可以在 Web请求级和方法调用级 来处理身份确认和授权。

2)SpringSecurity和Shiro的区别:

1.Shiro

Apache 旗下的轻量级权限控制框架

  • 轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求的互联网应用有更好表现

  • 通用性
    不局限于 Web 环境,可以脱离 Web 环境使用
    在 Web 环境下一些特定的需求需要手动编写代码定制。

2.Spring Security

Spring Security 是 Spring 家族中的一个安全管理框架

  • 和 Spring 无缝整合

  • 全面的权限控制

  • 专门为 Web 开发而设计

    旧版本不能脱离 Web 环境使用
    新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独
    引入核心模块就可以脱离 Web 环境。

  • 重量级

3)SpringSecurity的核心组件:

  1. Authentication(身份验证):封装用户的身份,通常使用其子类UsernamePasswordAuthenticationToken来封装用户名和密码进行身份验证。
  2. Authorization(授权):用于控制哪些用户具有哪些权限来访问应用程序中的特定资源。
  3. UserDetailsService:用于加载用户的真实信息(通常在mysql中获取数据),后续提供给AuthenticationManager跟Authentication进行身份验证。
  4. UserDetails:表示应用程序中的用户详细信息,如用户名、密码和权限等真实数据,UserDetailsService查询到数据的封装对象。
  5. AuthenticationManager:负责验证认证对象,并返回一个已认证的对象,通常包括使用多个 AuthenticationProvider。
  6. AuthenticationProvider:用于对用户进行身份验证,支持不同类型的身份验证机制,如用户名/密码、LDAP、OAuth 等。
  7. GrantedAuthority:表示用户所拥有的权限,通常由角色或权限字符串表示。
  8. SecurityContext:保存关联到当前执行线程的安全信息,包括已认证的主体和其权限,已经通过认证的Authentication对象载体。
  9. SecurityContextHolder:提供对当前线程的 SecurityContext 的访问,其中包含经过身份验证的用户的安全信息,统一管理SecurityContext对象,相当于SecurityContext对象的工厂类。
  10. FilterChain:用于处理传入的 HTTP 请求,Spring Security 将其用于应用一系列安全过滤器来执行身份验证和授权逻辑。
  11. SecurityConfigurerAdapter:用于配置 Spring Security 的 Java 配置适配器,允许用户自定义安全配置。
  12. AuthenticationEntryPoint:在用户试图访问受保护资源但未经过身份验证时触发的策略。
  13. LogoutHandler:处理用户登出时执行的操作,如清除会话、撤销令牌等。
  14. RememberMeServices:支持“记住我”功能,用于在用户下次访问应用程序时自动进行身份验证。
  15. PasswordEncoder:用于加密和验证密码,以确保安全存储用户凭据。
  16. SessionManagement:用于管理用户的会话,如设置最大并发会话数、会话创建和销毁事件等。
  17. CsrfTokenRepository:用于防止 CSRF(跨站请求伪造)攻击的令牌存储库。
  18. SecurityFilterChain:定义了一系列安全过滤器,用于保护特定的 URL 路径或请求模式。

4)SpringSecurity的工作流程:

SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。

过滤器过滤器作用默认是否加载
ChannelProcessingFilter过滤请求协议 HTTP 、HTTPSNO
WebAsyncManagerIntegrationFilter将 WebAsyncManger 与 SpringSecurity 上下文进行集成YES
SecurityContextPersistenceFilter在处理请求之前,将安全信息加载到 SecurityContextHolder 中YES
HeaderWriterFilter处理头信息加入响应中YES
CorsFilter处理跨域问题NO
CsrfFilter处理 CSRF 攻击YES
LogoutFilter处理注销登录YES
OAuth2AuthorizationRequestRedirectFilter处理 OAuth2 认证重定向NO
Saml2WebSsoAuthenticationRequestFilter处理 SAML 认证NO
X509AuthenticationFilter处理 X509 认证NO
AbstractPreAuthenticatedProcessingFilter处理预认证问题NO
CasAuthenticationFilter处理 CAS 单点登录NO
OAuth2LoginAuthenticationFilter处理 OAuth2 认证NO
Saml2WebSsoAuthenticationFilter处理 SAML 认证NO
UsernamePasswordAuthenticationFilter处理表单登录YES
OpenIDAuthenticationFilter处理 OpenID 认证NO
DefaultLoginPageGeneratingFilter配置默认登录页面YES
DefaultLogoutPageGeneratingFilter配置默认注销页面YES
ConcurrentSessionFilter处理 Session 有效期NO
DigestAuthenticationFilter处理 HTTP 摘要认证NO
BearerTokenAuthenticationFilter处理 OAuth2 认证的 Access TokenNO
BasicAuthenticationFilter处理 HttpBasic 登录YES
RequestCacheAwareFilter处理请求缓存YES
AwareRequestFilter包装原始请求YES
JaasApiIntegrationFilter处理 JAAS 认证NO
RememberMeAuthenticationFilter处理 RememberMe 登录NO
AnonymousAuthenticationFilter配置匿名认证YES
OAuth2AuthorizationCodeGrantFilter处理OAuth2认证中授权码NO
SessionManagementFilter处理 session 并发问题YES
ExceptionTranslationFilter处理认证/授权中的异常YES
FilterSecurityInterceptor处理授权相关YES
SwitchUserFilter处理账户切换NO

认证流程图:

根据认证流程我们能够了解到认证过程是在UsernamePasswordAuthenticationFilter过滤器中完成的:

  1. 认证过滤器会将前端输入的账号密码封装成UsernamePasswordAuthenticationToken,并交给AuthenticationManager进行认证处理;
  2. AuthenticationManager会调用AuthenticationProvider进行数据的一致性校验;
  3. AuthenticationProvider会调用UserDetailsServiceloadUserByUsername()方法获取真实数据UserDetails
  4. AuthenticationProvider会将UsernamePasswordAuthenticationTokenUserDetails进行数据一致性检验,并且添加权限;
  5. 校验通过会返回AuthenticationTokenUsernamePasswordAuthenticationFilter;
  6. UsernamePasswordAuthenticationFilterAuthenticationToken设置认证上下文中,表示完成认证。

授权流程图:

5)入门:

  1. 引入依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
  2. 配置账号密码:

    spring:
      security:
        user:
            name: admin
            password: 123456
  3. 网页输入http://localhost:8888/login:

6)自定义认证:

在真实项目中我们肯定是不会这样去使用的,目前比较流行的认证有3种:

  1. 登录后服务器保存session;
  2. 登陆后服务器返回给前端token;
  3. AUTH2.0的方式认证。

我们就采取token的方式来自定义认证方式吧;

具体流程:

6.1)自定义登录/登出接口:

@RestController
@RequestMapping("sys")
public class SysController {

    @Resource
    private AuthenticationManager authenticationManager;

    @PostMapping("login")
    public Result<String> login(@RequestBody LoginUser user) {
        System.out.println("自定义登录接口" + JSONUtil.toJsonStr(user));
        //1.将前端传入的数据封装成功SpringSecurity的认证对象
        MultiAuthenticationToken authenticationToken = new MultiAuthenticationToken(user.getUserName(), user.getPassword(), null, 1);
        //2.手动调用认证
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (authenticate == null) {
            return Result.fail("登录失败");
        }
        //生成token
        String token = IdUtil.fastSimpleUUID();
        //保存到缓存中
        Cache.TOKEN_CACHE.put(token, (UserInfo) authenticate.getDetails());
        //返回token
        return Result.success(token);
    }

    @PostMapping("logout")
    public Result<Boolean> logout(HttpServletRequest request) {
        //判断是否有token
        String authToken = request.getHeader("AuthToken");
        if (StrUtil.isBlank(authToken)) {
            throw new RuntimeException("AuthToken为空");
        }
        //获取当前用户
        MultiAuthenticationToken authentication = (MultiAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return Result.result(-1, false, "未登录");
        }
        //删除token缓存
        Cache.TOKEN_CACHE.remove(authToken);
        return Result.success(true);
    }
}

登录对象:

@Data
public class LoginUser {

    private String userName;

    private String password;
}

6.2)自定义多功能Authentication:

自定义一个既可以通过密码认证又可以短信认证的Authentication对象。

/**
 * @description: 多功能认证
 * @Title: MultiAuthenticationToken
 * @Author xlw
 * @Package com.xlw.test.spring_security_demo.config
 * @Date 2024/9/20 22:12
 */
public class MultiAuthenticationToken extends AbstractAuthenticationToken {

    private String principal;

    private String credentials;

    private String smsCode;

    /**
     * 登录类型,1:用户名密码登录,2:手机号登录
     */
    private int loginType;

    //认证前调用
    public MultiAuthenticationToken(String principal, String credentials, String smsCode, int loginType) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        this.smsCode = smsCode;
        this.loginType = loginType;
        setAuthenticated(false);
    }

    //确定认证时调用
    public MultiAuthenticationToken(String principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    @Override
    public String getCredentials() {
        return credentials;
    }

    @Override
    public String getPrincipal() {
        return principal;
    }

    public String getSmsCode() {
        return smsCode;
    }

    public int getLoginType() {
        return loginType;
    }

    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }
}

6.3)自定义AuthenticationProvider:

验证用户登录数据的入口。

/**
 * @description: 多功能认证校验器
 * @Title: MultiAuthenticationProvider
 * @Author xlw
 * @Package com.xlw.test.spring_security_demo.config
 * @Date 2024/9/20 22:22
 */
public class MultiAuthenticationProvider implements AuthenticationProvider {

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        if (authentication == null) {
            throw new AuthException("获取认证信息失败");
        }
        if (!(authentication instanceof MultiAuthenticationToken)) {
            throw new AuthException("认证对象不正确");
        }
        MultiAuthenticationToken authToken = (MultiAuthenticationToken) authentication;
        //获取真实的用户数据
        UserDetails userDetails = userDetailsService.loadUserByUsername(authToken.getPrincipal());
        if (userDetails == null) {
            throw new AuthException("用户名不存在");
        }
        if (authToken.getLoginType() == 1) {
            //密码登录
            if (!passwordEncoder.matches(authToken.getCredentials(), userDetails.getPassword())) {
                throw new AuthException("用户名或者密码不正确");
            }
        } else if (authToken.getLoginType() == 2) {
            //短信登录
            String code = Cache.SMS_CACHE.get(authToken.getPrincipal());
            if (StrUtil.isBlank(code)) {
                throw new AuthException("短信验证码已过期");
            }

            if (!Objects.equals(code, authToken.getSmsCode())) {
                throw new AuthException("短信验证码不正确");
            }
        }
        //设置详情
        authToken.setDetails(userDetails);
        return authToken;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return MultiAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

6.4)自定义UserDetailsService:

获取用户真实数据的类。

/**
 * @description:
 * @Title: AuthServiceImpl
 * @Author xlw
 * @Package com.xlw.test.spring_security_demo.service.impl
 * @Date 2024/9/20 21:58
 */
@Service
public class AuthServiceImpl implements UserDetailsService {

    /**
     * 按用户名加载用户
     *
     * @param username 用户名
     * @return {@link UserDetails }
     * @throws UsernameNotFoundException 未找到用户名异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //模拟从数据库取数据
        User user = Cache.DATABASE.get(username);
        if (user == null) {
            return null;
        }
        UserInfo userInfo = new UserInfo();
        BeanUtil.copyProperties(user, userInfo, false);
        return userInfo;
    }
}

6.5)自定义UserDetails:

封装用户的真实数据类

@NoArgsConstructor
@AllArgsConstructor
@Data
public class UserInfo implements UserDetails {

    private String username;

    private String password;

    private String realName;

    private Integer age;

    private Set<GrantedAuthority> authorities;

    private boolean accountNonExpired;

    private boolean accountNonLocked;

    private boolean credentialsNonExpired;

    private boolean enabled;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }
}

6.6)自定义PasswordEncoder:

对用户密码进行加密、匹配类

public class MD5PasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence rawPassword) {
        String pwd = DigestUtil.md5Hex(rawPassword.toString());
        return pwd;
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return Objects.equals(encodedPassword, encode(rawPassword));
    }
}

6.7)自定义认证失败处理器:

需要认证访问,却还未认证的访问请求进行失败处理。

public class LoginFailHandler implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.setContentType("application/json;charset=utf-8");
        Result<String> result = Result.fail(authException.getMessage());
        PrintWriter writer = response.getWriter();
        JSONConfig config = new JSONConfig();
        config.setIgnoreNullValue(false);
        config.setDateFormat(DatePattern.NORM_DATETIME_MS_PATTERN);
        try {
            writer.println(JSONUtil.toJsonStr(result, config));
        } finally {
            writer.flush();
            writer.close();
        }
    }
}

6.8)自定义token认证过滤器:

通过header携带的token检验该次请求能否认证。

public class TokenAuthFilter extends OncePerRequestFilter {

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authToken = request.getHeader("AuthToken");
        if (StrUtil.isNotBlank(authToken)) {
            UserInfo userInfo = Cache.TOKEN_CACHE.get(authToken);
            if (userInfo != null) {
                MultiAuthenticationToken authenticationToken = new MultiAuthenticationToken(userInfo.getUsername(), Collections.emptySet());
                //认证,相当于保存到ThreadLocal中供后续的拦截器使用,没有这一步说明认证失败
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        //放行,交给后面的过滤器处理
        filterChain.doFilter(request, response);
    }
}

6.9)配置security:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsService authService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置权限管理,白名单
        http.authorizeHttpRequests()
                .antMatchers(Cache.PATH_WHITELIST)
                .permitAll()
                .anyRequest()
                .authenticated() //别的请求都需要认证
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new LoginFailHandler());  //设置认证失败处理器
        //添加认证过滤器
        http.addFilterBefore(new TokenAuthFilter(), UsernamePasswordAuthenticationFilter.class);
        //禁用csrf和cors
        http.cors().and().csrf().disable();
        //禁用原始的表单登录
        http.formLogin().disable();
        //禁用原始的登出
        http.logout().disable();
        //禁用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    /**
     * 没有这个启动项目会报错
     * 认证管理器
     *
     * @return {@link AuthenticationManager }
     * @throws Exception 例外
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 配置验证器,加密等
     *
     * @param auth 认证
     * @throws Exception 例外
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(multiAuthenticationProvider())  //注入多功能认证器
                .userDetailsService(authService)    //注入用户详情服务
                .passwordEncoder(passwordEncoder());  //注入密码加密器
    }

    @Bean
    public AuthenticationProvider multiAuthenticationProvider() {
        return new MultiAuthenticationProvider();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new MD5PasswordEncoder();
    }
}

7)鉴权:

在SpringSecurity中有角色权限两种鉴权方式;

  • 定义角色要有ROLE_前缀,如:ROLE_ADMINROLE_SYSTEM等。
  • 定义权限没有特别规定,但是推荐模块:功能,如dept:listdept:add等。

SpringSecurity有两种鉴权方式:编程式注解式

首先我们将以上代码加入权限功能:

修改UserDetailsService

@Service
public class AuthServiceImpl implements AuthService, UserDetailsService {

    /**
     * 按用户名加载用户
     *
     * @param username 用户名
     * @return {@link UserDetails }
     * @throws UsernameNotFoundException 未找到用户名异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //模拟从数据库取数据
        User user = Cache.DATABASE.get(username);
        if (user == null) {
            return null;
        }
        UserInfo userInfo = new UserInfo();
        BeanUtil.copyProperties(user, userInfo, false);
        //权限集合
        Set<GrantedAuthority> authorities = new HashSet<>();
        //添加角色
        List<String> roles = Cache.USER_ROLE_DATABASE.get(userInfo.getUsername());
        if (CollectionUtil.isNotEmpty(roles)) {
            for (String role : roles) {
                GrantedAuthority authority = new SimpleGrantedAuthority(role);
                authorities.add(authority);
            }
        }
        //添加权限
        List<String> permissions = Cache.USER_PERMISSIONS_DATABASE.get(userInfo.getUsername());
        if (CollectionUtil.isNotEmpty(permissions)) {
            for (String permission : permissions) {
                GrantedAuthority authority = new SimpleGrantedAuthority(permission);
                authorities.add(authority);
            }
        }
        //设置用户拥有的权限
        userInfo.setAuthorities(authorities);
        return userInfo;
    }
}

修改TokenAuthFilter:

为MultiAuthenticationToken设置权限。

public class TokenAuthFilter extends OncePerRequestFilter {

    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String authToken = request.getHeader("AuthToken");
        if (StrUtil.isNotBlank(authToken)) {
            UserInfo userInfo = Cache.TOKEN_CACHE.get(authToken);
            if (userInfo != null) {
                //设置权限
                MultiAuthenticationToken authenticationToken = new MultiAuthenticationToken(userInfo.getUsername(), userInfo.getAuthorities());
                //认证,相当于保存到ThreadLocal中供后续的拦截器使用,没有这一步说明认证失败
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        //放行,交给后面的过滤器处理
        filterChain.doFilter(request, response);
    }
}

7.1)编程式鉴权:

支持4中鉴权方式:

**hasAnyAuthority()**:只要有任何一种权限即可访问;

//权限认证
http.authorizeHttpRequests().antMatchers("/sys/test").hasAnyAuthority("sys:list");

我们看到没有权限确实被拦截了但是却没有返回内,我们可以自定义AccessDeniedHandler来对鉴权失败的请求进行处理:

public class NotAccessHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        accessDeniedException.printStackTrace();
        response.setStatus(HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED);
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        try {
            JSONConfig config = new JSONConfig();
            config.setIgnoreNullValue(false);
            config.setDateFormat(DatePattern.NORM_DATETIME_MS_PATTERN);
            Result<String> result = new Result(HttpStatus.HTTP_PROXY_AUTH, null, "无权访问");
            writer.println(JSONUtil.toJsonStr(result, config));
        } finally {
            writer.flush();
            writer.close();
        }
    }
}

处理器生效配置:

//配置权限管理,白名单
http.authorizeHttpRequests()
        .antMatchers(Cache.PATH_WHITELIST)
        .permitAll()
        .anyRequest()
        .authenticated() //别的请求都需要认证
        .and()
        .exceptionHandling()
        .authenticationEntryPoint(new LoginFailHandler())  //设置认证失败处理器
        .accessDeniedHandler(new NotAccessHandler()); //设置无权访问处理器

再次访问试试:

设置sys:test权限:

http.authorizeHttpRequests().antMatchers("/sys/test").hasAnyAuthority("sys:list", "sys:test");

再次访问:

**hasAuthority()**:拥有某个权限才能访问;

//权限认证
http.authorizeHttpRequests().antMatchers("/sys/test").hasAuthority("sys:test");

**hasRole()**:拥有某个角色才能访问;

我们在定义角色时一定要加ROLE_前缀:

USER_ROLE_DATABASE.put("xlw", ListUtil.of("ROLE_ADMIN"));

我们在设置角色时候一定不要加ROLE_前缀,因为SpringSecurity会自动帮我们拼接上,因此ROLE_ADMIN设置hasRole()参数只要ADMIN即可,否则会报错:

//权限认证
http.authorizeHttpRequests().antMatchers("/sys/test").hasRole("ADMIN");

**hasAnyRole()**:拥有任一角色即可访问;

http.authorizeHttpRequests().antMatchers("/sys/test").hasAnyRole("ADMIN", "SYSTEM");

小妙招:hasAnyAuthority可以即包含某权限或者角色

>http.authorizeHttpRequests().antMatchers("/sys/test").hasAnyAuthority("sys:test", "ROLE_ADMIN");

7.2)注解式鉴权:

我们也可以通过注解的方式来进行鉴权,使用注解先要在启动类上加上:

@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)

@Secured

判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀ROLE_

@Secured({"ROLE_ADMIN"})
@GetMapping("test")
public Result<String> test() {
    return Result.success("ok");
}

@PreAuthorize

权限检验,请求到来访问控制单元之前必须包含xx权限才能访问,控制单元方法执行前进行角色校验。

支持权限和角色的校验:

  • hasRole(‘ROLE_NAME’):检查用户是否具有指定角色
  • hasAnyRole(‘ROLE1’, ‘ROLE2’):检查用户是否具有给定角色中的任意一个。
  • hasAuthority(‘AUTHORITY_NAME’):检查用户是否具有指定权限。
  • hasAnyAuthority(‘AUTHORITY1’, ‘AUTHORITY2’):检查用户是否具有给定权限中的任意一个。
  • hasPermission(targetObject, ‘permission’): 检查用户是否具有特定对象的特定权限。
//拥有角色
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("test")
public Result<String> test() {
    return Result.success("ok");
}
//拥有权限
@PreAuthorize("hasAuthority('sys:test')")
@GetMapping("test")
public Result<String> test() {
    return Result.success("ok");
}

@PostAuthorize

权限检验,请求到来访问控制单元之后必须包含xx权限才能访问 ,控制单元方法执行完后进行角色校验。

@PostAuthorize("hasRole('ADMIN')")
@GetMapping("test")
public Result<String> test() {
    return Result.success("ok");
}

@PostFilter

权限验证之后对数据进行过滤 留下用户名是 dingwen的数据。

需要开启 @EnableGlobalMethodSecurity(prePostEnabled = true)

@GetMapping("/postFilter")
@ResponseBody
@PreAuthorize("hasAnyRole('ROLE_管理员')")
@PostFilter("filterObject.username == 'dingwen'")
public List<UserEntity> postFilterTest() {
    List<UserEntity> userEntityList = new ArrayList<>();
    userEntityList.add(UserEntity.builder().username("test").build());
    userEntityList.add(UserEntity.builder().username("dingwen").build());
    return userEntityList;
}

@PreFilter

进入控制器之前对数据进行过滤,可对入参进行过滤

需要开启 @EnableGlobalMethodSecurity(prePostEnabled = true)

@GetMapping("/preFilter")
@ResponseBody
@Secured({"ROLE_管理员"})
@PreFilter(value = "filterObject.username == 'dingwen'")
public void preFilterTest(List<UserEntity> userEntityList){
    System.out.println();
}

文章作者: 威@猫
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 威@猫 !
评论
  目录