具体使用那种需要看自己项目的体量和具体的场景。
本案例中提供了五中方案及详细的解决办法,希望对你有帮助。
【方案1-通过session对象共享数据】
用户登录之后将用户的信息和权限信息放入到session中,然后再执行所有的操作中。获取session中存储的信息,如果信息存在就放行,如果信息不存在就不放行。
不足之处:每个方法中都需要通过session取值,冗余较大,不利于维护。
【方案2:通过aop的方式实现】
在springboot中通过@Pointcut注解配置切点,通过aop中的” 通知”实现在执行某方法前或后,先执行指定的通知。
好处:配置后,当我们执行目标方法的时候,通知会自动的执行。
好处:只需要写一份代码,便于维护。
【方案3:通过拦截器实现】
在springboot中配置拦截器,实现请求拦截,也能实现验证和授权功能
【方案4:通过过滤器实现】
过滤器也可以实现登录及权限操作,但是不建议。
【方案5:通过专门的权限框架实现】
推荐使用:功能齐全,安全性高,不但能够实现验证、授权还可以实现加密、会话管理、缓存等。
shiro权限框架:老牌框架,功能丰富。
spring Security:简单好用,但是必须使用spring框架。
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private String use_rid;
private String user_name;
}
@Controller
public class StudentController {
//查询数据的时候判断,用户是否已经登录或者是否有权限。
//以前的操作业务如下
@RequestMapping("/showInfoOld")
@ResponseBody
public void showInfoOld(HttpSession session){
System.out.println("============showInfoOld=============");
//通过session值获取用户的登录信息和权限信息
Employee employee=(Employee)session.getAttribute("employee");
if(employee==null){
//说明用户没有登录,重定向到登录页面中
}else{
//表示用户登录,重定向到你想去的地方
}
}
}
本质:通过spring中通知的方式实现
前置通知:执行目标代码前执行
环绕通知:执行目标代码前和代码后都执行
后置通知:执行目标代码后执行
最终通知:在目标方法执行之后执行的通知
异常通知:程序抛出异常后执行
springboot2.7.10中默认的aop包版本就是1.9.7
org.aspectj
aspectjweaver
1.9.7
runtime
org.aspectj
aspectjrt
1.9.7
runtime
注意点1:如果想获取session中的值,直接通过注解注入就行,下面有使用方法
注意点2:案例中,把五中通知类型的方法都放进去了,按照自己的需求使用,单个测试。
@Component
@Aspect
public class EmployeeAspect {
@Autowired
HttpSession session;//配置使用session
@Autowired
HttpServletRequest request;//配置使用request
@Autowired
HttpServletResponse response;//配置使用response
//配置切点,切点就是告诉程序,那些方法需要使用到某功能
@Pointcut("execution(* com.txc.springbootpointcut.controller..*.*(..))")
public void pointCut(){}
/*
* 类型:前置通知
* 配置通知的类型,通知类型有前置通知,后置通知,环绕通知,异常通知,最终通知
* 如:前置通知就是@Before修饰,在执行你的方法之前,先执行我指定的方法
* 通知的本质就是:拦截器
* JoinPoint能够获取我们执行的是哪个方法
* @Before("pointCut()"):pointCut()是上面定义的切点方法
* */
@Before("pointCut()")
public void before(JoinPoint joinPoint) throws IOException {
System.out.println("=============执行了前置通知===========");
Employee employee=(Employee) session.getAttribute("employee");
if(employee!=null){
//放行
}else{
//重定向,可以通过request或response等重定向
response.sendRedirect("/showInfoOld");
}
}
/*
* 类型:环绕通知
* */
@Around("pointCut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("===========环绕通知开始=============");
proceedingJoinPoint.proceed();
System.out.println("===========环绕通知结束=============");
}
/*
* 最终通知
* */
@After("pointCut()")
public void after(JoinPoint joinPoint){
//获取方法名
String methodName = joinPoint.getSignature().getName();
//获取方法的参数
Object[] args = joinPoint.getArgs();
//获取username
System.out.println("==========后置通知执行=========="+session.getAttribute("username"));
}
/*
* 类型:异常通知
* 时机:发生异常的时候执行的方法
* */
@AfterThrowing(value = "pointCut()", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
System.out.println("=====异常信息=====" + e.getMessage());
}
/*
* 类型:后置通知
* */
@AfterReturning("pointCut()")
public void afterReturning(){
System.out.println("=============后置通知=============");
}
}
StudentController中showInfoNew方法执行的时候,会自动触发aop,因为aop中定义了切点如下。
@Pointcut("execution(* com.txc.springbootpointcut.controller..*.*(..))")
@Controller
public static class StudentController {
//我们可以通过aop的思想实现,更加的简便,利于维护
@RequestMapping("/showInfoNew")
@ResponseBody
public void showInfoNew(HttpSession session){
session.setAttribute("username","晓春");
System.out.println("=======执行了showInfoNew方法========");
}
}
}
本质:需要通过springboot整合使用拦截器
重点:Springboot中使用拦截器需要实现WebMvcConfigurer 接口,重写addInterceptors方法,并注入。
案例中使用IDEA+springboot2.7.9+jdk1.8+mysql版本开发
选择springboot版本+springbweb依赖
org.springframework.boot
spring-boot-starter-parent
2.7.9
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
1.16.14
4.4、自定义类实现拦截器HandlerInterceptor 接口
案例说明:访问资源的时候拦截器实现获取session中user的值,如果值不存在,通过request或response就重定向
如果值存在就放行
preHandle:返回值为false不放行,返回值为true表示放行
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("===============拦截器===============");
//从session中获取user的信息
User user = (User) request.getSession().getAttribute("user");
//判断用户是否登录
if (null == user) {
response.sendRedirect("user/error");//可以写你想重定向的路径
//request.setAttribute("msg", "请先登录");
//request.getRequestDispatcher("/login.html").forward(request, response);
return false;
}
return true;
}
}
4.5、将自定义拦截器注入到系统中
需要实现WebMvcConfigurer 类重写addInterceptors方法
重点:自定义配置类InterceptorConfig 需要使用注解@Component进行实例,否则不生效
addPathPatterns
:
设置拦截器拦截的请求地址,
/**表示拦截所有的请求
excludePathPatterns
:设置拦截器不想拦截的地址,
"/user/login",
"/error"表示不拦截的地址
registry.addInterceptor
:将自定义拦截器注入进
WebMvcConfigurer配置中
@Component
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
String[] addPathPatterns = {//拦截所有请求
"/**"
};
String[] excludePathPatterns = {//设置不想拦截的路径
"/user/login","/error"
};
registry.addInterceptor(new UserInterceptor()).addPathPatterns(addPathPatterns).excludePathPatterns(excludePathPatterns);
}
}
@SpringBootApplication
@ComponentScan(basePackages ="com.txc")
public class SpringinterceptorApplication {
public static void main(String[] args) {
SpringApplication.run(SpringinterceptorApplication.class, args);
}
}
说明:此时当我们访问下面的test方法时候,会优先执行拦截器InterceptorConfig中的addInterceptors方法
@Controller
public class UserController {
@RequestMapping("/test")
public String test(){
System.out.println("=========test===========");
return "";
}
}
创建springboot工程,导入springweb开发包
doFilter:中写了一个常用的登录校验,如果正在登录就放行,如果登录过放行,如果没有登录就重定向。
filterChain.doFilter(servletRequest, servletResponse):表示放行
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("=======过滤器执行========");
HttpServletRequest request=(HttpServletRequest)servletRequest;
HttpServletResponse response=(HttpServletResponse)servletResponse;
HttpSession session=request.getSession();
//获取当前的请求地址
String servletpath=request.getServletPath();
System.out.println(servletpath);
if(servletpath.equals("/login.do") || servletpath.equals("/login.html")){
filterChain.doFilter(servletRequest, servletResponse);
}else{
String username=(String)session.getAttribute("username");
if(username==null || username.equals("")){
response.sendRedirect("login.html");
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
@Component
public class FilterConfig {
//注册三大组件--Filter
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean filter=new FilterRegistrationBean();
filter.setFilter(new MyFilter());
// /*表示过滤所有请求 /myfilter:过滤指定请求
filter.setUrlPatterns(Arrays.asList("/*","/myfilter"));
return filter;
}
}
应用场景不同:
拦截器:更多用于性能分析、日志记录、权限检查、登录验证、处理cookie、本地化、国际化
过滤器:通用对请求的URL进行过滤,对敏感词汇进行过滤、设置字符编码、压缩响应信
执行的时机不同:
拦截器:进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束
过滤器:在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后
过滤器在拦截器之前执行
依赖不同:
拦截器:不依赖tomcat容器,可以单独使用
过滤器:是Servlet的一部分,有依赖性
实现原理不同:
拦截器:使用动态代理等技术
过滤器:使用xml解析和servlet容器等技术
如下案例实现登录、校验、授权、动态菜单管理+数据库设计
https://blog.csdn.net/tangshiyilang/article/details/130089485