1.自定义用户登录认证
思想:用户登录后将用户存入到ThreadLocal中,访问非白名单接口时在filter中获取用户信息,为空则抛出异常,不为空放行。
1.存用户信息
package com.ztccloud.auth.util;
import com.ztccloud.auth.entity.RecordUser;
/**
* @program: zfd-questionaire
* @description: session管理器
* @author: dct
* @create: 2022-01-26 10:56
**/
public class SessionUtils {
//当前线程存放用户信息
public static ThreadLocal<RecordUser> threadLocal = new ThreadLocal<>();
/**
* 设置用户信息
*
* @param recordUser
*/
public static void setUser(RecordUser recordUser) {
threadLocal.set(recordUser);
}
/**
* 获取用户信息
*
* @return
*/
public static RecordUser getUser() {
RecordUser user = threadLocal.get();
return user;
}
/**
* 删除用户信息
*/
public static void distoryUser() {
threadLocal.remove();
}
}
对存入信息进行二次封装:
package com.ztccloud.auth.util;
import com.ztccloud.auth.entity.RecordUser;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpSession;
/**
* @author dongchentong
* @date 2021/11/15 18:03
*/
@Component
public class UserControl {
public static final String SESSION_USER="user";
public static void setUser(RecordUser user){
HttpSession session = getSession();
session.setAttribute(SESSION_USER, user);
SessionUtils.setUser(user);
}
public static void removeUser() {
HttpSession session = getSession();
session.removeAttribute(SESSION_USER);
SessionUtils.distoryUser();
}
private static HttpSession getSession() {
HttpSession session = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getSession();
return session;
}
}
3.核心filter
package com.ztccloud.auth.filter;
import com.ztccloud.auth.entity.RecordUser;
import com.ztccloud.auth.util.SessionUtils;
import com.ztccloud.auth.util.UserControl;
import com.ztccloud.naire.exception.ErrorController;
import com.ztccloud.naire.exception.QuestionException;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* @program: zfd-questionaire
* @description: 用户认证拦截器
* @author: dct
* @create: 2022-01-26 10:48
**/
public class AuthFilter implements Filter {
//配置白名单
protected static List<String> patterns = Arrays.asList("/user/login");
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
String url = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
if (patterns.contains(url)|| StringUtils.containsAny(url,new String[]{"swagger","api-docs"})) {
//在白名单中的url,放行访问
filterChain.doFilter(httpRequest, httpResponse);
return;
}
RecordUser user=(RecordUser) httpRequest.getSession().getAttribute(UserControl.SESSION_USER);
if (user != null) {
//若为登录状态 放行访问
UserControl.setUser(user);
filterChain.doFilter(httpRequest, httpResponse);
} else {
//提示用户未进行登录
try {
throw new QuestionException("ques_user_1001","User not logged in!");
} catch (Exception e) {
servletRequest.setAttribute(ErrorController.ATTRIBUTE, e);
servletRequest.getRequestDispatcher(ErrorController.ERROR_PATH).forward(servletRequest, servletResponse);
}
}
}
@Override
public void destroy() {
}
}
4.请求结束后使用监听器,释放Session中的数据
package com.ztccloud.auth.filter;
import com.ztccloud.auth.util.SessionUtils;
import org.springframework.stereotype.Component;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
/**
* @program: zfd-questionaire
* @description: 请求监听器, 释放线程内存
* @author: dct
* @create: 2022-01-26 14:24
**/
@Component
public class RequestListener implements ServletRequestListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
SessionUtils.distoryUser();
}
}
2.使用自定义注解进行方法级别权限校验
1.定义角色枚举类
package com.ztccloud.auth.node;
import java.util.HashMap;
import java.util.Map;
/**
* @program: zfd-questionaire
* @description: 角色枚举
* @author: dct
* @create: 2022-01-26 17:08
**/
public enum UserRoles {
/**
* 1,管理员
*/
ADMIN("1000","ADMIN", "超级用户"),
/**
* 2,二级角色
*/
SECADMIN("2000","SECADMIN", "二级角色"),
/**
* 3.基础角色
*/
DOMESTICCONSUMER("3000","DOMESTICCONSUMER", "基础角色");
/**
* RoleID
*/
private String id;
/**
* RoleName
*/
private String code;
/**
* 中文名称
*/
private String name;
UserRoles(String id, String code, String name) {
this.id = id;
this.code = code;
this.name = name;
}
}
2.定义注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @program: zfd-questionaire
* @description: 自定义注解interface
* @author: dct
* @create: 2022-01-26 17:32
**/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization
{
UserRoles[] value() default{};
}
3.授权处理拦截器
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
/**
* @program: zfd-questionaire
* @description: 授权处理拦截器
* @author: dct
* @create: 2022-01-26 17:46
**/
@Component("authInterceptor")
public class AuthInterceptor implements HandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
{
if (handler.getClass().isAssignableFrom(HandlerMethod.class))
{
// 方法上是否有授权注解
HandlerMethod handlerMethod = (HandlerMethod) handler;
Authorization authorization = handlerMethod.getMethodAnnotation(Authorization.class);
if(authorization == null){
return true;
}
// 获取授权信息
ArrayList<UserRoles> roles = new ArrayList<UserRoles>();
for (int i = 0; i < authorization.value().length; i++)
{
roles.add(authorization.value()[i]);
}
// 权限检查时,若权限不匹配,需抛出异常
AuthChecker.check(roles, request);
}
return true;
}
}
4.实时权限校验
import com.ztccloud.auth.entity.Role;
import com.ztccloud.auth.util.SessionUtils;
import com.ztccloud.naire.exception.QuestionException;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @program: zfd-questionaire
* @description: 实时权限检查
* @author: dct
* @create: 2022-01-26 17:33
**/
public class AuthChecker {
/**
* @Description: 执行权限检查。根据Controller的权限和实际登录用户的权限进行匹配,从而允许或者拒 绝访问。拒绝访问时,抛出异常
* @Param: [presetRoles, request]
* @return: [java.util.List<com.ztccloud.auth.node.UserRoles>, javax.servlet.http.HttpServletRequest]
* @Author: dct
* @Date: 2022/1/26
*/
public static void check(List<UserRoles> presetRoles, HttpServletRequest request)
{
// 若允许匿名访问,不做权限检查。
List<Role> userRoles = SessionUtils.getUser().getUserRoles();
List<String> roles = presetRoles.stream().map(UserRoles::name).collect(Collectors.toList());
List<String> roleNames = userRoles.stream().map(Role::getRoleName).collect(Collectors.toList());
if(!Collections.disjoint(roles,roleNames)) {
return;
} else {
throw new QuestionException("ques_user_1003","Insufficient permissions");
}
}
}
5.注册Hanlder
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @program: zfd-questionaire
* @description: 角色认证器注册
* @author: dct
* @create: 2022-01-26 21:51
**/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/**");
}
}