package cn.lili.security; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.security.AuthUser; import cn.lili.common.security.enums.PermissionEnum; import cn.lili.common.security.enums.SecurityEnum; import cn.lili.common.security.enums.UserEnums; import cn.lili.common.security.token.SecretKeyUtil; import cn.lili.common.utils.ResponseUtil; import cn.lili.modules.member.entity.dos.Clerk; import cn.lili.modules.member.service.ClerkService; import cn.lili.modules.member.service.StoreMenuRoleService; import cn.lili.modules.member.token.StoreTokenGenerate; import com.google.gson.Gson; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.util.PatternMatchUtils; import org.springframework.web.bind.annotation.RequestMethod; import javax.naming.NoPermissionException; 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.ArrayList; import java.util.List; import java.util.Map; /** * @author Chopper */ @Slf4j public class StoreAuthenticationFilter extends BasicAuthenticationFilter { private final Cache cache; private final StoreTokenGenerate storeTokenGenerate; private final StoreMenuRoleService storeMenuRoleService; private final ClerkService clerkService; public StoreAuthenticationFilter(AuthenticationManager authenticationManager, StoreTokenGenerate storeTokenGenerate, StoreMenuRoleService storeMenuRoleService, ClerkService clerkService, Cache cache) { super(authenticationManager); this.storeTokenGenerate = storeTokenGenerate; this.storeMenuRoleService = storeMenuRoleService; this.clerkService = clerkService; this.cache = cache; } @SneakyThrows @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { //从header中获取jwt String jwt = request.getHeader(SecurityEnum.HEADER_TOKEN.getValue()); //如果没有token 则return if (StrUtil.isBlank(jwt)) { chain.doFilter(request, response); return; } //获取用户信息,存入context UsernamePasswordAuthenticationToken authentication = getAuthentication(jwt, response); //自定义权限过滤 if (authentication != null) { customAuthentication(request, response, authentication); SecurityContextHolder.getContext().setAuthentication(authentication); } chain.doFilter(request, response); } /** * 获取token信息 * * @param jwt * @param response * @return */ private UsernamePasswordAuthenticationToken getAuthentication(String jwt, HttpServletResponse response) { try { Claims claims = Jwts.parser() .setSigningKey(SecretKeyUtil.generalKeyByDecoders()) .parseClaimsJws(jwt).getBody(); //获取存储在claims中的用户信息 String json = claims.get(SecurityEnum.USER_CONTEXT.getValue()).toString(); AuthUser authUser = new Gson().fromJson(json, AuthUser.class); //校验redis中是否有权限 if (cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.MEMBER, authUser.getId()) + jwt) || cache.hasKey(CachePrefix.ACCESS_TOKEN.getPrefix(UserEnums.STORE, authUser.getId()) + jwt) ) { //用户角色 List auths = new ArrayList<>(); auths.add(new SimpleGrantedAuthority("ROLE_" + authUser.getRole().name())); //构造返回信息 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser.getUsername(), null, auths); authentication.setDetails(authUser); return authentication; } ResponseUtil.output(response, 403, ResponseUtil.resultMap(false, 403, "登录已失效,请重新登录")); return null; } catch (ExpiredJwtException e) { log.debug("user analysis exception:", e); } catch (Exception e) { log.error("user analysis exception:", e); } return null; } /** * 自定义权限过滤 * * @param request 请求 * @param response 响应 * @param authentication 用户信息 */ private void customAuthentication(HttpServletRequest request, HttpServletResponse response, UsernamePasswordAuthenticationToken authentication) throws NoPermissionException { AuthUser authUser = (AuthUser) authentication.getDetails(); String requestUrl = request.getRequestURI(); //如果不是超级管理员, 则鉴权 if (Boolean.FALSE.equals(authUser.getIsSuper())) { String permissionCacheKey = CachePrefix.PERMISSION_LIST.getPrefix(UserEnums.STORE) + authUser.getId(); //获取缓存中的权限 Map> permission = (Map>) cache.get(permissionCacheKey); // if (permission == null || permission.isEmpty()) { //根据会员id查询店员信息 Clerk clerk = clerkService.getClerkByMemberId(authUser.getId()); if (clerk != null) { permission = storeTokenGenerate.permissionList(storeMenuRoleService.findAllMenu(clerk.getId(), authUser.getId())); cache.put(permissionCacheKey, permission); } //} //获取数据(GET 请求)权限 if (request.getMethod().equals(RequestMethod.GET.name())) { //如果用户的超级权限和查阅权限都不包含当前请求的api if (match(permission.get(PermissionEnum.SUPER.name()), requestUrl) || match(permission.get(PermissionEnum.QUERY.name()), requestUrl)) { } else { ResponseUtil.output(response, ResponseUtil.resultMap(false, 400, "权限不足")); log.error("当前请求路径:{},所拥有权限:{}", requestUrl, JSONUtil.toJsonStr(permission)); throw new NoPermissionException("权限不足"); } } //非get请求(数据操作) 判定鉴权 else { if (!match(permission.get(PermissionEnum.SUPER.name()), requestUrl)) { ResponseUtil.output(response, ResponseUtil.resultMap(false, 400, "权限不足")); log.error("当前请求路径:{},所拥有权限:{}", requestUrl, JSONUtil.toJsonStr(permission)); throw new NoPermissionException("权限不足"); } } } } /** * 校验权限 * * @param permissions 权限集合 * @param url 请求地址 * @return 是否拥有权限 */ boolean match(List permissions, String url) { if (permissions == null || permissions.isEmpty()) { return false; } return PatternMatchUtils.simpleMatch(permissions.toArray(new String[0]), url); } }