自学内容网 自学内容网

自定义注解+拦截器+jwtFilter实现权限控制

自定义注解和拦截器配置方式参考:https://blog.csdn.net/qq_35201802/article/details/143782854

jwtFilter 过滤器配置参考:https://blog.csdn.net/qq_35201802/article/details/143694195

这里主要讲权限控制方面的内容

定义权限枚举

package com.shore.my_spring_demo.common.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum RolesEnum {

    SUPER_ADMIN(1, "超级管理员"),
    SYSTEM_ADMIN(2, "系统管理员"),
    DOMESTIC_CONSUMER(3, "普通用户"),
    VISITOR(4, "游客"),

    UNKNOWN(999, "未知用户")
    ;

    private final int code;
    private final String value;

    public static RolesEnum getByCode(int code) {
        for (RolesEnum rolesEnum : RolesEnum.values()) {
            if (rolesEnum.getCode() == code) {
                return rolesEnum;
            }
        }
        return UNKNOWN;
    }

    public static RolesEnum getByValue(String value) {
        for (RolesEnum rolesEnum : RolesEnum.values()) {
            if (rolesEnum.getValue().equals(value)) {
                return rolesEnum;
            }
        }
        return UNKNOWN;
    }
}

新建 service 构建 UserDetail 实体类

加入权限字段,我这里为了测试固定写为 1, 实际项目中可从数据库中查

package com.shore.my_spring_demo.web.filters;

import com.shore.my_spring_demo.common.enums.RolesEnum;
import com.shore.my_spring_demo.dal.entity.UserEntity;
import com.shore.my_spring_demo.dal.repo.UserServiceRepo;
import com.shore.my_spring_demo.service.user.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Service;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class UserDetailService implements UserDetailsService {
    @Resource
    private UserServiceRepo userServiceRepo;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        UserEntity userEntity = userServiceRepo.getUserByUsername(s);
        int role = 1;
        return new UserDetails() {
            // 用户被授予的所有权限集合
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                Set<GrantedAuthority> authorities = new HashSet<>();
                authorities.add(new SimpleGrantedAuthority(RolesEnum.getByCode(role).getValue()));
                return authorities;
            }

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

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

            // 检查用户账号是否过期
            @Override
            public boolean isAccountNonExpired() {
                return true;
            }

            // 检查用户账户是否锁住
            @Override
            public boolean isAccountNonLocked() {
                return true;
            }

            // 检查用户凭据是否过期
            @Override
            public boolean isCredentialsNonExpired() {
                return true;
            }

            // 检查用户是否被启用
            @Override
            public boolean isEnabled() {
                return true;
            }
        };
    }
}

新增注解

注解有两个字段,flag 用来判断进行哪种类型的校验,如果是权限校验,则通过 allow 配置访问接口需要的权限身份

package com.shore.my_spring_demo.common.annoation;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Verification {
    int flag() default 0;

    int[] allow() default {1, 2};
}

过滤器

jwtFIlter 校验用户 token,从 token 中获取用户信息,用户权限,写入上下文中

package com.shore.my_spring_demo.web.filters;

import com.shore.my_spring_demo.common.enums.ErrorEnums;
import com.shore.my_spring_demo.exception.UsersException;
import com.shore.my_spring_demo.service.jwt.JwtService;
import com.shore.my_spring_demo.web.config.CustomAuthFilterConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

//import com.shore.my_spring_demo.common.utils.JwtUtil;

@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Resource
    private UserDetailService userDetailsService;

    @Resource
    private JwtService jwtService;

    @Resource
    private CustomAuthFilterConfig customConfig;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        List<String> allowedURI = customConfig.getExcludes();
        if (null == allowedURI || allowedURI.isEmpty()) {
            log.info("未找到跳过检查的接口路径配置,使用默认配置,跳过登陆,注册,查询接口");
            allowedURI = Arrays.asList("/shore/demo/user/login", "/shore/demo/user/register", "/shore/demo/user/query");
        }
        if (allowedURI.contains(request.getRequestURI())) {
            log.info("URI: {} 不需要身份校验", request.getRequestURI());
            filterChain.doFilter(request, response);
            return;
        }
        String header = request.getHeader("Authorization");
        if (null == header || !header.startsWith("Bearer")) {
            log.error("header: {}, token 信息不存在", header);
            throw new UsersException(ErrorEnums.FAIL_VALIDATE);
        }
        String token = header.substring(6); // 去除"Bearer "前缀

        // 验证JWT的有效性
        if (!jwtService.validateTokenAsymmetric(token)) {
            log.error("token: {}, 校验失败", token);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        }
        // 从JWT中获取用户名
        String username = jwtService.getUsernameFromTokenAsymmetric(token);

        // 从UserDetailsService加载用户信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        // 如果用户信息存在,创建一个认证对象,并将其存储在安全上下文中
        if (userDetails != null) {
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        log.info("token 校验通过,username:{}", username);
        // 继续过滤器链
        filterChain.doFilter(request, response);
    }
}

拦截器配置

拦截器中实现具体的权限校验逻辑

package com.shore.my_spring_demo.common.interceptor;

import com.shore.my_spring_demo.common.annoation.Verification;
import com.shore.my_spring_demo.common.enums.RolesEnum;
import com.shore.my_spring_demo.common.enums.VerificationFlagEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Component
public class VerificationInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        return HandlerInterceptor.super.preHandle(request, response, handler);
        if (handler instanceof HandlerMethod handlerMethod) {
            Method method = handlerMethod.getMethod();
            if (method.isAnnotationPresent(Verification.class)) {
                Verification annotation = method.getAnnotation(Verification.class);
                VerificationFlagEnum.getByCode(annotation.flag());
                switch (VerificationFlagEnum.getByCode(annotation.flag())) {
                    case NO_VER -> {
                        log.info("跳过校验");
                    }
                    case LOGIN_VER -> {
                        log.info("登陆校验");
                    }
                    case TOKEN_VER -> {
                        log.info("token校验");
                    }
                    case PERMISSION_VER -> {
                        log.info("权限校验");
                        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                        if (null == authentication || null == authentication.getPrincipal() || !(authentication.getPrincipal() instanceof UserDetails userDetails)) {
                            log.error("上下文中无用户信息");
                            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                            return false;
                        }
                        int[] allow = annotation.allow();
                        List<Integer> roles = userDetails.getAuthorities().stream()
                                .map(authority -> RolesEnum.getByValue(authority.getAuthority()).getCode())
                                        .toList();
                        if (!Arrays.stream(allow).anyMatch(roles::contains)) {
                            log.error("无访问权限");
                            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                            return false;
                        }
                        return true;
                    }
                    default -> {
                    }
                }
                return true;
            }
        }
        return true;
    }
}

验证

接口允许 2,3身份的用户访问,用户实际身份权限是1,拒绝访问

@Verification(flag = 3, allow = {2, 3})
    @PostMapping("/update")
    public ApiResponse<Boolean> updateUser(@RequestBody UserReq req) {
        return ApiResponse.success(userService.updateUser(req));
    }

注解上增加权限1则可以正常访问


原文地址:https://blog.csdn.net/qq_35201802/article/details/143783848

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!