权限设计、用户登录校验

准备数据

CREATE TABLE `user` (
  `name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `role` (
  `name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `privilege` (
  `name` varchar(255) NOT NULL,
  `uri` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `user_role` (
  `user_name` varchar(255) NOT NULL,
  `role_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `role_privilege` (
  `role_name` varchar(255) NOT NULL,
  `privilege_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入数据

INSERT INTO `user` VALUES ('u1');
INSERT INTO `user` VALUES ('u2');

INSERT INTO `role` VALUES ('r1');
INSERT INTO `role` VALUES ('r2');
INSERT INTO `role` VALUES ('r3');

INSERT INTO `privilege` VALUES ('p1', '/p1');
INSERT INTO `privilege` VALUES ('p2', '/p2');
INSERT INTO `privilege` VALUES ('p3', '/p3');
INSERT INTO `privilege` VALUES ('p4', '/p4');

INSERT INTO `user_role` VALUES ('u1', 'r1');
INSERT INTO `user_role` VALUES ('u1', 'r2');
INSERT INTO `user_role` VALUES ('u2', 'r2');
INSERT INTO `user_role` VALUES ('u2', 'r3');

INSERT INTO `role_privilege` VALUES ('r1', 'p1');
INSERT INTO `role_privilege` VALUES ('r1', 'p2');
INSERT INTO `role_privilege` VALUES ('r2', 'p2');
INSERT INTO `role_privilege` VALUES ('r2', 'p3');
INSERT INTO `role_privilege` VALUES ('r3', 'p3');
INSERT INTO `role_privilege` VALUES ('r3', 'p4');

此时如果要查询某一个用户对应的角色和权限,sql如下

select
	u.name user_name,r.name role_name,p.name privilege_name,p.uri uri
from
	user u
INNER JOIN
	user_role ur ON u.name = ur.user_name
INNER JOIN
	role r on ur.role_name = r.name
INNER JOIN
	role_privilege rp on r.name = rp.role_name
INNER JOIN
	privilege p on rp.privilege_name = p.name
where
	u.name = #{username}   

1.通过Filter实现用户登录校验和权限校验

在业务中,只有用户登录了,才能访问服务器中的资源

分析:

  • 1.判断是登录后,才能访问的资源

    • 是:比如各种接口,比如某些jsp资源,判断用户是否登录
    • 否:比如login.jsp,、loginServlet、以及一些css/js文件,直接放行
  • 2.判断当前用户是否登录,判断session中是否有user

    • 有,用户已经登录,放行.(进行用户权限校验)
    • 否.跳转登录页面

(1)用户登录校验

package demo.filter;

import demo.pojo.User;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    // 登录校验
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        // 获取资源请求路径
        String uri = request.getRequestURI();

        // 判断当前访问资源,是否是是登录后,才能访问的资源
        String contextPath = request.getContextPath();
        if (uri.equals(contextPath + "/login.jsp") || uri.equals(contextPath + "/login")
                || uri.startsWith(contextPath + "/css/") || uri.startsWith(contextPath + "/js/")) {
            // 在request中添加一个标识,告诉后面的过滤器,直接放行
            request.setAttribute("filterPass", true);
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // 此资源需要用户登录后才能访问
            User user = (User)request.getSession().getAttribute("user");
            if(user != null){
                //登录了。放行
                filterChain.doFilter(servletRequest, servletResponse);
            }else{
                //没有登录。跳转登录页面(使用重定向或者请求转发都可以实现)
                // request.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
                HttpServletResponse response = (HttpServletResponse)servletResponse;
                response.sendRedirect(contextPath + "/login.jsp");
            }
        }

    }

    @Override
    public void destroy() {

    }
}

xml

<!-- 登录过滤器 -->
  <filter>
    <filter-name>loginFilter</filter-name>
    <filter-class>demo.filter.LoginFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>loginFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

登录页面

package demo.controller;

import demo.mapper.UserMapper;
import demo.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;

@Controller
public class UserController {

    @Autowired
    private UserMapper userMapper;
    
    
    // 跳转到users.jsp
    @RequestMapping(value = "login")
    public String toUsers(@RequestParam("name")String name, HttpServletRequest request) {
        User user = userMapper.queryUserByName(name);
        request.getSession().setAttribute("user", user);
        return "success";
    }

    // 跳转到users.jsp
    @RequestMapping(value = "p1")
    public String p1(HttpServletRequest request) {
        return "p1";
    }

    // 跳转到users.jsp
    @RequestMapping(value = "p2")
    public String p2(HttpServletRequest request) {
        return "p2";
    }

    // 跳转到users.jsp
    @RequestMapping(value = "p3")
    public String p3(HttpServletRequest request) {
        return "p3";
    }

    // 跳转到users.jsp
    @RequestMapping(value = "p4")
    public String p4(HttpServletRequest request) {
        return "p4";
    }

}


(2)通过filter实现权限控制

package demo.filter;

import demo.pojo.Privilege;
import demo.pojo.Role;
import demo.pojo.User;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PrivilegeFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    // 登录校验
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        
		// 获取标识,判断是否为不需要进行权限校验的资源 
        Boolean pass = (Boolean)request.getAttribute("filterPass");
        if (null == pass || !pass.equals(true)) {
            User user = (User)request.getSession().getAttribute("user");
            
            // 判断用户是否有访问此资源的权限
            Set<String> uri = new HashSet<>();
            List<Role> roles = user.getRoles();
            for (Role role : roles) {
                List<Privilege> privileges = role.getPrivileges();
                for (Privilege privilege : privileges) {
                    uri.add(request.getContextPath() + privilege.getUri());
                }
            }
			// 用户的所有权限包含当前访问资源的uri
            if (uri.contains(request.getRequestURI())){
                filterChain.doFilter(servletRequest, servletResponse);
            } else {
                HttpServletResponse response = (HttpServletResponse)servletResponse;
                response.sendRedirect(request.getContextPath() + "/login.jsp");
            }
            
        } else {
        	// 放pass为true时,此资源不用权限校验,直接放行
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

因为在filter中,我们无法知道请求对应的是哪个controller中的哪个方法,所以我们只能通过用户和uri来进行权限控制


2.通过拦截器实现用户登录校验和权限校验

首先我们看下拦截器的如何被调用的。

Web请求被DispatcherServlet截获后,会调用DispatcherServlet的doDispatcher方法。
在这里插入图片描述
当我们访问一个uri,映射器找不到对应的handler时,此时返回的HandlerExecutionChain为null(虽然我们设置的拦截器的路径为/**,拦截所有,但其实拦截器是通过映射器来调用的,只有当映射器找到具体的handler时,才会去找对应的拦截器)

我们在通常情况下,设置DispatcherServlet的url为/,仅处理访问handler的请求,映射器也是如此,它无法去映射到一些静态资源,例如xx.jsp、xx.html、xx.css

也就是说拦截器只能拦截通过映射器找到的handler,映射器无法映射到的资源,它都是不能拦截的,例如xx.jsp、xx.html、xx.css等等

所以在实现用户登录校验时,我们只要关注那些handler(对应的uri)是可以通行的


(1)使用拦截器实现用户登录校验
用户没有登录时,仅能访问/login所对应的handler

package demo.interceptors;

import demo.pojo.User;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

public class LoginInterceptor  implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String uri = request.getRequestURI();
        // 如果是login放行
        if (uri.equals(request.getContextPath() + "/login")) {
            return true;
        } else{
            User user = (User)request.getSession().getAttribute("user");
            if(user != null){
                //登录了。放行
                return true;
            }else{
                //没有登录。跳转登录页面(使用重定向或者请求转发都可以实现)
                response.sendRedirect(request.getContextPath() + "/login.jsp");
                return false;
            }
        }
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}

path="/**"表示拦截所有层级的路径

<!-- 注册自定义的拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截所有请求 -->
        <mvc:mapping path="/**"/>
        <!-- 自定义拦截器的全路径 -->
        <bean class="demo.interceptors.LoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

(2)使用拦截器实现权限校验

package demo.interceptors;

import demo.pojo.Privilege;
import demo.pojo.Role;
import demo.pojo.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PrivilegeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String uri = request.getRequestURI();
        // 如果是login放行
        if (uri.equals(request.getContextPath() + "/login")) {
            return true;
        } else{
            //进行权限校验
            User user = (User)request.getSession().getAttribute("user");
            Set<String> uris = new HashSet<>();
            List<Role> roles = user.getRoles();
            for (Role role : roles) {
                List<Privilege> privileges = role.getPrivileges();
                for (Privilege privilege : privileges) {
                    uris.add(request.getContextPath() + privilege.getUri());
                }
            }
            if (uris.contains(request.getRequestURI())){
                return true;
            } else {
                response.sendRedirect(request.getContextPath() + "/login.jsp");
                return false;
            }
        }
    }


    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }
}

<!-- 注册自定义的拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 拦截所有请求 -->
        <mvc:mapping path="/**"/>
        <!-- 自定义拦截器的全路径 -->
        <bean class="demo.interceptors.LoginInterceptor"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- 拦截所有请求 -->
        <mvc:mapping path="/**"/>
        <!-- 自定义拦截器的全路径 -->
        <bean class="demo.interceptors.PrivilegeInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

3.总结过滤器和拦截器差别

1.过滤器和拦截器的的触发时机不同,过滤器是进入容器后,但请求进入servlet之前进行预处理的,返回也是在servlet处理完后进行返回的,两者的执行顺序如下图
在这里插入图片描述2.拦截器可以获取IOC容器中的各个bean,而过滤器就不行,因为拦截器是spring提供并管理的,spring的功能可以被拦截器使用,在拦截器里注入一个service,可以调用业务逻辑。而过滤器是JavaEE标准,只需依赖servlet api ,不需要依赖spring。
在这里插入图片描述
3.拦截器可以知道请求对应的是哪个handler(哪个Controller中的哪个method),而过滤器是无法知道的
4.过滤器可以拦截所有的资源,拦截器一般只拦截handler

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值