Spring Security基于动态角色资源权限校验

Posted kezp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security基于动态角色资源权限校验相关的知识,希望对你有一定的参考价值。

Spring Security(2)基于动态角色权限校验


在上一章我们了解到了 Security 是怎么进行基础配置的,下面我们要进行对数据库访问和权限分配的操作。 如果还不了解怎么进行基本配置请查阅 [Spring Security(1)入门体验](https://www.cnblogs.com/kezp/p/13237180.html)
这一节主要使用权限资源对请求权限进行管理,也是前后端分离最常用的权限校验方式。希望大家看完会有所收获。


数据库结构设计

要实现权限控制首先得把数据库设计好。


技术图片


我这边是这么设计的有一些字段可能比较多。仅供参考。


Security 配置调整

我们之前做了一些基础的配置,现在在这个基础的配置上进行改造。一步步打造成我们需要的功能。

1、将登录成功、登录失败、退出登录的处理类进行提取


2.1、登录成功处理类

AdminAuthenticationSuccessHandler

@Component
public class AdminAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        // SecurityUser 是Spring Security里面UserDetailsd的实现类里面包含了用户信息
        SecurityUser securityUser = ((SecurityUser) authentication.getPrincipal());
        // 从SecurityUser 中取出Token
        String token = securityUser.getToken();
		// 定义一个map集合 用于返回JSON数据
        HashMap<String, String> map = new HashMap<String, String>(1);
        // 前端会收到 {"token":"token值"} 
        map.put("token",token);
        // 结果集封装 规范返回内容
        Object result = ApiResult.ok(map);
        // 将封装数据进行返回。 这里有一个返回工具类
        ResponseUtils.renderJson(httpServletResponse, JacksonUtil.toJson(result));
    }
}

2.2、SecrityUser 类

上面提到了 SecurityUser 类 ,这里贴一下我的代码
SecurityUser

// 注解 避免JSON返回不必要的空数据
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecurityUser implements UserDetails {
    /**
     * 当前登录用户
     */
    private User currentUserInfo;

    /**
     * 角色
     */
    private List<Role> roleList;

    /**
     * 用户权限值
     */
    private List<Permission> permissionList;


    private String token;


    /**
     * 空参构造
     */
    public SecurityUser() {
    }

    /**
     * 用户信息构造
     *
     * @param user
     */
    public SecurityUser(User user) {
        if (user != null) {
            this.currentUserInfo = user;
        }
    }

    public SecurityUser(User user, List<Role> roleList) {
        if (user != null) {
            this.currentUserInfo = user;
            this.roleList = roleList;
        }
    }

    public SecurityUser(User user, List<Role> roleList, List<Permission> permissionList) {
        if (user != null) {
            this.currentUserInfo = user;
            this.roleList = roleList;
            this.permissionList = permissionList;
        }
    }


    @Override
    public String getPassword() {
        return currentUserInfo.getPassword();
    }

    @Override
    public String getUsername() {
        return currentUserInfo.getUsername();
    }

    /**
     * 用户是否过期
     *
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    /**
     * 用户是否被锁定
     *
     * @return true 用户被锁定  false用户没有被锁定
     */
    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    /**
     * 账户登录凭证是否过期
     *
     * @return
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    /**
     * 用户是否被禁用
     *
     * @return true是  false否
     */
    @Override
    public boolean isEnabled() {
        return false;
    }


    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public User getCurrentUserInfo() {
        return currentUserInfo;
    }

    public List<Role> getRoleList() {
        return roleList;
    }

    public List<Permission> getPermissionList() {
        return permissionList;
    }


    /**
     * 获取当前用户具有的角色
     * 用户添加权限。返回权限对象
     * 这个方法很重要,会影响到后面的权限资源控制
     * @return GrantedAuthority
     */
    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new HashSet<>();
        // 判断权限列表是否为空
        if (!CollectionUtils.isEmpty(this.permissionList)) {
            // 对权限列表进行遍历
            for (Permission permission : this.permissionList) {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission.getAccessCode());
                // 将权限添加到集合中
                authorities.add(authority);
            }
        }
        //返回权限
        return authorities;
    }
}

2.3、登录失败处理方法

AdminAuthenticationFailureHandler

@Slf4j
@Component
public class AdminAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        Object result;
        // 对异常信息进行封装处理返回给前端
        if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) {
            result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),e.getMessage());
        } else if (e instanceof LockedException) {
            result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"账户被锁定,请联系管理员!");
        } else if (e instanceof CredentialsExpiredException) {
            result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"证书过期,请联系管理员!");
        } else if (e instanceof AccountExpiredException) {
            result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"账户过期,请联系管理员!");
        } else if (e instanceof DisabledException) {
            result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"账户被禁用,请联系管理员!");
        } else {
            log.error("登录失败:", e);
            result = ApiResult.fail(HttpStatus.PAYMENT_REQUIRED.code(),"登录失败!");
        }
        ResponseUtils.renderJson(response, JacksonUtil.toJson(result));
    }
}

2.4、退出登录处理

AdminLogoutSuccessHandler

@Component
public class AdminLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        Object result = ApiResult.ok("退出成功");
        ResponseUtils.renderJson(httpServletResponse, result);
    }
}

2、Security 配置类

SecurityConfig

@EnableWebSecurity
// 开启注解权限资源控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 自定义认证处理器 (重点:额外添加的类,用于获取用户信息,保存用户信息。并且处理登录等操作)
     */
    private final AdminAuthenticationProvider adminAuthenticationProvider;

    /**
     * 登录成功处理器
     */
    private final AdminAuthenticationSuccessHandler adminAuthenticationSuccessHandler;

    /**
     * 登录失败处理器
     */
    private final AdminAuthenticationFailureHandler adminAuthenticationFailureHandler;

    /**
     * 未登录情况下的处理类 (额外添加的类 这个类在下方有说明)
     */
    private final AdminAuthenticationEntryPoint adminAuthenticationEntryPoint;

    /**
     * Token 处理器 (额外添加的类 这个类在下方有说明)
     */
    private final MyTokenAuthenticationFilter myTokenAuthenticationFilter;

    /**
     * 退出登录处理类 (我在这里处理了一下缓存的token退出的时候清除掉了,这个类下方也有说明)
     */
    private final AdminLogoutHandler adminLogoutHandler;

    /**
     * 退出成功处理
     */
    private final AdminLogoutSuccessHandler adminLogoutSuccessHandler;

    // 上面是登录认证相关  下面为url权限相关 - ========================================================================================

    /**
     * 登陆过后无权访问返回处理类 (返回无权的时候的返回信息处理)
     */
    private final MyAccessDeniedHandler myAccessDeniedHandler;

    public SecurityConfig(AdminAuthenticationProvider adminAuthenticationProvider, AdminAuthenticationSuccessHandler adminAuthenticationSuccessHandler, AdminAuthenticationFailureHandler adminAuthenticationFailureHandler, AdminAuthenticationEntryPoint adminAuthenticationEntryPoint, MyTokenAuthenticationFilter myTokenAuthenticationFilter, AdminLogoutHandler adminLogoutHandler, AdminLogoutSuccessHandler adminLogoutSuccessHandler, MyAccessDeniedHandler myAccessDeniedHandler) {
        this.myTokenAuthenticationFilter = myTokenAuthenticationFilter;
        this.adminAuthenticationProvider = adminAuthenticationProvider;
        this.adminAuthenticationSuccessHandler = adminAuthenticationSuccessHandler;
        this.adminAuthenticationFailureHandler = adminAuthenticationFailureHandler;
        this.adminAuthenticationEntryPoint = adminAuthenticationEntryPoint;
        this.adminLogoutHandler = adminLogoutHandler;
        this.adminLogoutSuccessHandler = adminLogoutSuccessHandler;
        this.myAccessDeniedHandler = myAccessDeniedHandler;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();

        // 标记只能在 服务器本地ip[127.0.0.1或者localhost] 访问`/home`接口,其他ip地址无法访问
        // registry.antMatchers("/").hasIpAddress("127.0.0.1");

        // 允许匿名的url - 可理解为放行接口 - 多个接口使用,分割
        registry.antMatchers("/home").permitAll();

        // OPTIONS(选项):查找适用于一个特定网址资源的通讯选择。 在不需执行具体的涉及数据传输的动作情况下, 允许客户端来确定与资源相关的选项以及 / 或者要求, 或是一个服务器的性能
        // registry.antMatchers(HttpMethod.OPTIONS, Constants.CONTEXT_PATH+"/**").denyAll();

        // 其余所有请求都需要认证
        registry.anyRequest().authenticated();

        // 禁用CSRF 开启跨域
        http.csrf().disable();

        // 开启CSRF 向前端发送 XSRF-TOKEN Cookie(上面设置了关闭,可以通过下述方式进行开启,但是swagger-ui的跨域403异常暂时找不到解决办法。无奈放弃了)
        // http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());

        //配置HTTP基本身份认证
        http.httpBasic();

        // 未登录认证异常
        http.exceptionHandling().authenticationEntryPoint(adminAuthenticationEntryPoint);

        // 登陆过后无权访问返回
        http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);

        // 登录处理 - 前后端一体的情况下
        http.formLogin().loginProcessingUrl("/user/login")
                    // 默认的登录成功返回url
                    // .defaultSuccessUrl("/")
                    // 登录成功处理
                    .successHandler(adminAuthenticationSuccessHandler)
                    .failureHandler(adminAuthenticationFailureHandler)
                    // 自定义登陆用户名和密码属性名,默认为 username和password
                    .usernameParameter("username").passwordParameter("password")
                    // 异常处理
                    // .failureUrl(Constants.CONTEXT_PATH+"/login/error").permitAll()
                    // 退出登录
                    .and().logout().logoutUrl("/user/logout").permitAll()
                    .addLogoutHandler(adminLogoutHandler)
                    .logoutSuccessHandler(adminLogoutSuccessHandler);

        // session创建规则  STATELESS 不使用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 防止iframe 造成跨域
        http.headers().frameOptions().disable();

        // 添加前置的过滤器 用于验证token
        http.addFilterBefore(myTokenAuthenticationFilter, BasicAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 自定义验证管理器
        auth.authenticationProvider(adminAuthenticationProvider);
        // super.configure(auth);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // ignoring 允许添加 RequestMatcher Spring Security 应该忽略的实例。
        web.ignoring().antMatchers(HttpMethod.GET,
                "/favicon.ico",
                "/*.html",
                "/**/*.css",
                "/**/*.js");
        web.ignoring().antMatchers(HttpMethod.GET,"/swagger-resources/**");
        web.ignoring().antMatchers(HttpMethod.GET,"/webjars/**");
        web.ignoring().antMatchers(HttpMethod.GET,"/v2/api-docs");
        web.ignoring().antMatchers(HttpMethod.GET,"/v2/api-docs-ext");
        web.ignoring().antMatchers(HttpMethod.GET,"/configuration/ui");
        web.ignoring().antMatchers(HttpMethod.GET,"/configuration/security");
    }
}

3、自定义认证处理器

用于从获取用户信息、保存用户信息,并且处理登录等操作。

AdminAuthenticationProvider

@Component
public class AdminAuthenticationProvider implements AuthenticationProvider {

    private final
    UserDetailsServiceImpl userDetailsService;

    private final RedisUtil redisUtil;

    @Autowired
    public AdminAuthenticationProvider(UserDetailsServiceImpl userDetailsService, RedisUtil redisUtil) {
        this.userDetailsService = userDetailsService;
        this.redisUtil = redisUtil;
    }
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取前端表单中输入后返回的用户名、密码
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();

        // 通过用户名称获取用户信息 (UserDetailsServiceImpl 这个类是实现了 Security 中提供的 UserDetailsService 用于处理用户信息 具体内容在下方)
        SecurityUser userInfo = (SecurityUser) userDetailsService.loadUserByUsername(userName);

        // 判断用户是否存在
        if(userInfo == null){
            throw new UsernameNotFoundException("当前用户名不存在");
        }
        // 对可能出现的异常进行抛出,抛出的一次会跳到登录失败中进行集中处理
        if(userInfo.isEnabled()){
            throw new DisabledException("该账户已被禁用,请联系管理员");
        }else if(userInfo.isAccountNonLocked()){
            throw new LockedException("该账户已被锁定");
        }else if(userInfo.isAccountNonExpired()){
            throw new AccountExpiredException("该账户已过期,请联系管理员");
        }else if(userInfo.isCredentialsNonExpired()){
            throw new CredentialsExpiredException("该账户的登陆凭证已过期,请重新登录");
        }

        // 密码验证,用于验证传进来的密码和加密后的密码是否是同一个。
        boolean isValid = PasswordUtils.checkpw(password, userInfo.getPassword());
        if (!isValid) {
            //抛出密码异常
            throw new BadCredentialsException("密码错误!");
        }

        // 前后端分离情况下 处理逻辑...
        // 更新登录令牌 - 之后访问系统其它接口直接通过token认证用户权限...
        String token = PasswordUtils.hashpw(System.currentTimeMillis() + userInfo.getUsername(), PasswordUtils.getSalt());
        userInfo.setToken(token);
        // 通过 redis 缓存数据
        redisUtil.set(token,userInfo,30*60L);

        return new UsernamePasswordAuthenticationToken(userInfo,password,userInfo.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> aClass) {
        //确保 aClass 能转成该类
        return aClass.equals(UsernamePasswordAuthenticationToken.class);
    }
}


上面有提到所以贴出来方便理解整个过程,这个类用于处理用户数据,获取用户信息。

UserDetailsServiceImpl

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserService userService;

    private final UserRoleService userRoleService;

    private final RoleService roleService;

    private final RolePermissionService rolePermissionService;

    private final PermissionService permissionService;

    @Autowired(required = false)
    public UserDetailsServiceImpl(UserMapper userMapper, RoleMapper roleMapper, PermissionMapper permissionMapper, UserRoleMapper userRoleMapper, RolePermissionMapper rolePermissionMapper, GroupPermissionMapper groupPermissionMapper, UserService userService, UserRoleService userRoleService, RoleService roleService, RolePermissionService rolePermissionService, PermissionService permissionService) {
        this.userService = userService;
        this.userRoleService = userRoleService;
        this.roleService = roleService;
        this.rolePermissionService = rolePermissionService;
        this.permissionService = permissionService;
    }

    /**
     * 根据用户获取用户信息、角色信息、权限信息
     *
     * @param username:
     * @return UserDetails
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从数据库中取出用户信息
        User user = userService.queryByUserName(username);
        // 判断用户是否存在
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        List<Role> userRoles = getUserRoles(user.getId());
        List<Permission> userRolePermissions = getUserRolePermissions(userRoles);
        // 返回 SecurityUser 这个类上面有介绍
        return new SecurityUser(user,userRoles,userRolePermissions);
    }

    /**
     * 根据用户ID获取角色权限信息
     *
     * @param id
     * @return
     */
    private List<Role> getUserRoles(Integer id) {
        //通过用户ID查询角色中间表
        List<UserRole> userRoles = userRoleService.queryAll(id);
        //通过角色中间表查询角色列表
        List<Role> roles = roleService.queryByUserRoles(userRoles);
        // 返回角色列表
        return roles;
    }

    /**
     * 通过角色列表获取权限信息字符串
     *
     * @return
     */
    private Set<String> getUserRolePermissionsAccessCode(List<Role> roleList) {
        /**
         * 通过角色权限中间表查询
         */
        List<RolePermission> rolePermissions = rolePermissionService.queryByRoleList(roleList);

        return permissionService.queryByPermissionIdsOrAccessCode(rolePermissions);
    }

    /**
     * 通过角色列表获取权限信息
     *
     * @return
     */
    private List<Permission> getUserRolePermissions(List<Role> roleList) {
        /**
         * 通过角色权限中间表查询
         */
        List<RolePermission> rolePermissions = rolePermissionService.queryByRoleList(roleList);

        return permissionService.queryByPermissionIds(rolePermissions);
    }
}

4、用户 Token 处理

MyTokenAuthenticationFilter

@Slf4j
@Component
public class MyTokenAuthenticationFilter extends OncePerRequestFilter {

    private final RedisUtil redisUtil;

    public MyTokenAuthenticationFilter(RedisUtil redisUtil) {
        this.redisUtil = redisUtil;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        log.info("请求头类型: {}" , httpServletRequest.getContentType());

        MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
        MultiReadHttpServletResponse wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            // 记录请求的消息体
            logRequestBody(wrappedRequest);

            // 前后端分离情况下,前端登录后将token储存在cookie中,每次访问接口时通过token去拿用户权限
            // Constants 这个类是自己定义的常量类REQUEST_HEADER这个表示token头的名称
            String token = wrappedRequest.getHeader(Constants.REQUEST_HEADER);
            log.debug("后台检查令牌:{}", token);

            if (StringUtils.isNotBlank(token)) {
                // 检查token
                SecurityUser securityUser = (SecurityUser) redisUtil.get(token);
                if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
                    throw new AccessDeniedException("TOKEN已过期,请重新登录!");
                }
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());

                // 全局注入角色权限信息和登录用户基本信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } finally {
            stopWatch.stop();
            long usedTimes = stopWatch.getTotalTimeMillis();
            // 记录响应的消息体
            logResponseBody(wrappedRequest, wrappedResponse, usedTimes);
        }
    }
    
	// 处理请求
    private void logRequestBody(MultiReadHttpServletRequest request) {
        MultiReadHttpServletRequest wrapper = request;
        if (wrapper != null) {
            try {
                String bodyJson = wrapper.getBodyJsonStrByJson(request);
                String url = wrapper.getRequestURI().replace("//", "/");
                log.info("-------------------------------- 请求url: " + url + " --------------------------------");
                Constants.URL_MAPPING_MAP.put(url, url);
                log.info("`{}` 接收到的参数: {}",url , bodyJson);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
	// 处理返回
    private void logResponseBody(MultiReadHttpServletRequest request, MultiReadHttpServletResponse response, long useTime) {
        MultiReadHttpServletResponse wrapper = response;
        if (wrapper != null) {
            byte[] buf = wrapper.getBody();
            if (buf.length > 0) {
                String payload;
                try {
                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
                } catch (UnsupportedEncodingException ex) {
                    payload = "[unknown]";
                }
                log.info("`{}`  耗时:{}ms  返回的参数: {}", Constants.URL_MAPPING_MAP.get(request.getRequestURI()), useTime, payload);
                log.info("");
            }
        }
    }
}

到这里其实已经基本上差不多了。


5、其他处理类


5.1、未登录情况下的处理类

AdminAuthenticationEntryPoint.java

@Slf4j
@Component
public class AdminAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        log.error(e.getMessage());
        // 其中HttpStatus类是自己定义的一个枚举类。用于定义状态码和消息
        ResponseUtils.renderJson(httpServletResponse, ApiResult.fail(HttpStatus.UNAUTHORIZED.code(),HttpStatus.UNAUTHORIZED.reasonPhraseCN()));
    }
}

5.2、登陆过后无权访问返回处理类

MyAccessDeniedHandler.java

@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        ResponseUtils.renderJson(httpServletResponse, ApiResult.fail(403, e.getMessage()));
    }
}

到这里配置项已经完成了。


6、在 Controller 中添加访问权限

这里我简单写了一下 UserController.java

@RestController
@RequestMapping("/api/user")
@Api(value = "用户相关操作",tags="用户相关操作")
public class UserController {

    private final
    UserMapper userMapper;

    @Autowired(required = false)
    public UserController(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
	
    // 需要注意这里的权限控制值,需要与数据库中获取到的值一致
    @PreAuthorize("hasAuthority(‘user‘)")
    @PostMapping("/userInfo")
    @ApiOperation(value = "查询用户信息",notes = "查询用户信息请求头里面需要携带X-Token")
    UserInfo getUserInfo() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        UserInfo userInfo = null;
        if(authentication != null){
            SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
            userInfo = new UserInfo();
            userInfo.setUser(securityUser.getCurrentUserInfo());
            userInfo.setRoles(securityUser.getRoleList());
            userInfo.setPermissions(securityUser.getPermissionList());
            userInfo.setPermissionAccessList(securityUser.getPermissionAccessCode());
        }
        return userInfo;
    }


    @PreAuthorize("hasAuthority(‘user_add‘)")
    @PostMapping("/addUser")
    @ApiOperation(value = "添加用户",notes = "添加用户信息")
    @ApiImplicitParam()
    Object addUser(@RequestBody User user){
        user.setPassword(PasswordUtils.hashpw(user.getPassword(),PasswordUtils.getSalt()));
        userMapper.insertSelective(user);
        return ApiResult.ok("操作成功");
    }

    @PreAuthorize("hasAuthority(‘no_access‘)")
    @PostMapping("/noAccess")
    @ApiImplicitParam()
    Object noAccess(){
        return ApiResult.ok("无权访问的接口");
    }
}

需要注意: @PreAuthorize("hasAuthority(‘user‘)")? 该注解里面的值需要与数据库里面的值保持一致



对上述的功能进行测试

写了这么多,也要对实际结果进行确认才行。我这里用的 Postman 测试一下。

首先需要登录获取 token

技术图片

通过登录接口可以拿到token,token值需要放到请求头中。用于认证用户是否登录。


再测试一下其他接口


  • 获取用户信息(注意这里设置了 token 请求头)
    技术图片

  • 访问无权接口
    技术图片


这样的话就差不多是最终的效果了。多尝试多进步,方法总比困难多。
如果有更好的处理方式可以提出来相互交流~












以上是关于Spring Security基于动态角色资源权限校验的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security 基于用户的权限? (不基于角色)

在 Spring Security 3 中动态创建新角色和权限

spring security动态管理资源结合自定义登录页面

基于Spring Boot+Security+Redis权限管理系统,权限控制采用RBAC

如何使用 LDAP 和基于角色的数据库授予权限来实现 Spring Security?

Spring Security和Apache Shiro开发企业级权限管理系统