项目亮点之权限管理
权限管理
- 如果是在service层或者dao层,直接使用spring的AOP切面编程
- 如果是在controller层,因为需要request或response,因此一般采用filter功率器或者spring的Interceptor拦截器。
前置知识
DispatchServlet流程
Filter、Interceptor对比
Filter | Interceptor | |
---|---|---|
实现方式 | 基于函数回调 | 基于Java反射 |
Servlet容器 | 依赖 | 不依赖 |
SpringMVC框架 | 不依赖 | 依赖 |
作用范围 | 所有请求 | action请求 |
调用次数 | 容器初始化时一次 | Action的生命周期多次调用 |
操作对象 | request、response | request、response、handler、modelAndView、exception |
执行顺序 | 先过滤 | 后拦截 |
Filter、Interceptor、Aspect对比
过滤器(Filter) :可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息。
拦截器(Interceptor):可以拿到你请求的控制器和方法,却拿不到请求方法的参数。
切片 (Aspect): 可以拿到方法的参数,但是却拿不到http请求和响应的对象
SSO单点登录
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的。
-
实现方式
-
session广播机制
-
流程:用户在某个模块登录后,用户信息存在这个模块session中,然后在其他模块进行session复制
-
缺点:虽然可以实现SSO,但是假如项目有几十个模块,就要复制几十次session,极大的消耗资源资源。
-
-
cookie+redis实现
-
流程:在项目中任何一个模块进行登录,登录之后,把数据放到两个地方:
- redis:在key:生成唯一随机值,在value:存放用户数据
- cookie:把redis里面生成的key值放到cookie里面
-
访问项目中访问其他模块时,发送请求带着cookie进行发送,获取cookie值,到redis根据key进行查询,如果查询到数据就是 登录,查不到就没有登录。
-
-
token实现
- 流程:在项目某个模块进行登录,登录之后,按照规则生成字符串,把登录之后用户信息包含到生成的字符串里面,把①字符串通过地址栏返回②把字符串通过cookie返回,再去访问其他模块时,在地址栏带着生成的字符串,在访问模块里面获取地址栏字符串,根据字符串获取用户信息,如果可以获取到就是登录。
- 伪造token怎么解决
- http+redis
-
Aop
基于动态代理实现,无法获取http请求和响应的对象
@Aspect
@Component
public class PerformanceAspect {
private static final Logger log = LoggerFactory.getLogger(PerformanceAspect.class);
@Around("execution(* com.bmw.seed.controller.*.*(..))")
public Object logTome(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
String method = pjp.getSignature().getName();
String className = pjp.getTarget().getClass().getName();
Object ret = pjp.proceed();
log.info("func<doAround> method<" + className + "." + method + "> cost time <"
+ (System.currentTimeMillis() - begin) + ">ms");
return ret;
}
}
Filter
Filter在SpringBoot项目中可以采用基于注解自动配置和手动配置两种方式
-
编写过滤器
@WebFilter(filterName = "authFilter", urlPatterns = "/*") public class AuthFilter implements Filter { private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); //拦截url规则 @Value("${auth.login.pattern}") private String urlPattern; //登录接口 @Value("${auth.login.loginUrl}") private String loginUrl; //业务接口前缀 @Value("${auth.login.host}") private String host; /** * 过滤器,如果存在user属性放行,否则会把重定向到登录接口并添加参数url为被过滤的请求(ip+路径+参数) * * @param servletRequest 请求 * @param servletResponse 响应 * @param filterChain 链 * @author 石一歌 * @date 2022/7/19 23:33 */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; //请求去域名部分的路径 String uri = httpServletRequest.getRequestURI(); log.info("uri:" + uri); if (uri.matches(urlPattern)) { String user = (String) httpServletRequest.getSession().getAttribute("user"); if (user != null) { filterChain.doFilter(httpServletRequest, httpServletResponse); } else { String requestUrl = getRequestUrl(httpServletRequest); httpServletResponse.sendRedirect(loginUrl + "?url=" + requestUrl); } return; } filterChain.doFilter(httpServletRequest, httpServletResponse); } private String getRequestUrl(HttpServletRequest httpServletRequest) throws UnsupportedEncodingException { String requestURI = host + httpServletRequest.getRequestURI(); String queryString = httpServletRequest.getQueryString(); if (StringUtils.isEmpty(queryString)) { return requestURI; } else { StringBuilder stringBuffer = new StringBuilder(); stringBuffer.append(URLEncoder.encode(requestURI + "?", "UTF-8")); String[] qsArray = queryString.split("&"); String[] qsPair; if (qsArray.length > 0) { for (String s : qsArray) { qsPair = s.split("="); if (qsPair.length == 2) { stringBuffer .append(qsPair[0]) .append(URLEncoder.encode(requestURI + "=", "UTF-8")) .append(qsPair[1]) .append(URLEncoder.encode(requestURI + "&", "UTF-8")); } } } return String.valueOf(stringBuffer); } } }
-
开启过滤器
@ServletComponentScan(basePackages = "com.bmw.seed.filter")
-
配置
auth: login: pattern: '/?([-|\w]+)?test/(\w)+' loginUrl: http://127.0.0.1/mock/login host: http://127.0.0.1
Interceptor
-
编写拦截器
@Component public class LoginInterceptor implements HandlerInterceptor { private static final Logger log = LoggerFactory.getLogger(LoginInterceptor.class); //拦截url规则 @Value("${auth.login.pattern}") private String urlPattern; //登录接口 @Value("${auth.login.loginUrl}") private String loginUrl; //业务接口前缀 @Value("${auth.login.host}") private String host; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //请求去域名部分的路径 String uri = request.getRequestURI(); log.info("uri:" + uri); if (uri.matches(urlPattern)) { String user = (String) request.getSession().getAttribute("user"); if (user != null) { return true; } else { String requestUrl = getRequestUrl(request); response.sendRedirect(loginUrl + "?url=" + requestUrl); return false; } } return true; } private String getRequestUrl(HttpServletRequest httpServletRequest) throws UnsupportedEncodingException { String requestURI = host + httpServletRequest.getRequestURI(); String queryString = httpServletRequest.getQueryString(); if (StringUtils.isEmpty(queryString)) { return requestURI; } else { StringBuilder stringBuffer = new StringBuilder(); stringBuffer.append(URLEncoder.encode(requestURI + "?", "UTF-8")); String[] qsArray = queryString.split("&"); String[] qsPair; if (qsArray.length > 0) { for (String s : qsArray) { qsPair = s.split("="); if (qsPair.length == 2) { stringBuffer .append(qsPair[0]) .append(URLEncoder.encode(requestURI + "=", "UTF-8")) .append(qsPair[1]) .append(URLEncoder.encode(requestURI + "&", "UTF-8")); } } } return String.valueOf(stringBuffer); } } }
-
开启拦截器
@Configuration public class LoginAdapter implements WebMvcConfigurer { @Resource private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor).addPathPatterns("/**"); } }
Shiro
SpringSecurity
- 认证 (确认身份)
- 授权 (授予权限)
- 攻击防护 (防止伪造身份)