自定义注解+拦截器+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)!