没学会 Spring Aop?带你手撸一套 RBAC 风格的 AOP 权限系统框架

小七学习网,助您升职加薪,遇问题可联系:客服微信【1601371900】 备注:来自网站

RBAC(基于角色的权限控制),模型的核心是在用户和权限之间引入了角色的概念,取消了用户和权限的直接关联,改为通过用户关联角色、角色关联权限的方法来间接地赋予用户权限,从而达到用户和权限解耦的目的。 …

RBAC(基于角色的权限控制),模型的核心是在用户和权限之间引入了角色的概念,取消了用户和权限的直接关联,改为通过用户关联角色、角色关联权限的方法来间接地赋予用户权限,从而达到用户和权限解耦的目的。

用户和管理员的权限肯定是不一样的,管理员能对用户进行操作并给予权限,用户权限小于管理员并能根据管理员给予的权限获得一定的操作空间。其中的权限分配到底该怎么实现呢?本次我们就以大名鼎鼎的安全框架 Apache Shiro 为模板,以 AOP 的形式去写一个自己的权限框架。

主要内容:

  1. RBAC 简介
  2. 数据库该怎么设计
  3. 搭建基本架构
  4. 如何生成代码,避免重复造轮子
  5. 基本 Web 页面实现
  6. 认证与授权的实现
  7. 实现用户权限鉴定
  8. 换个方式鉴权:使用 AOP 鉴权
  9. 实现一个 Realm
  10. 登陆踢人的原理及实现
  11. 添加过滤器
  12. 源码链接


RBAC 简介

RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便——RBAC 介绍。

我们这次来模仿着做一个给予 RBAC 的权限框架,来探究一下登录过程的真正实现。同时我也加入了 Spring AOP 相关的知识,一起实现整个项目功能。

数据库设计与解析

设计

首先我们要先做一个数据库的环境搭建,我们一共需要五张表分别是 :

  • permission
  • role
  • role_permission
  • user
  • user_role

也就是权限表、角色表、权限角色关联表、用户表、用户角色关联表

一定要明白这句话:

每个用户都可以拥有一个或多个角色,每个角色又可以拥有一个或多个权限。

在我们匹配到用户的时候,会先提取用户的角色信息,再根据角色信息去匹配对应的权限,从而实现用户、角色、权限的绑定。

下面以我的数据库为例,新建数据库 mysecurity:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

解析

先来简单讲下这个数据库的各个表。

role_permission 和 user_role 是中间表,user 存的用户信息 admin 的 id 为 1,以此 id 去 role_permission 表里可以看到,id 为 1 的用户共有 4 个权限分别对应 1234,且在 role 表里 id 为 1 的而用户角色是管理员,此时再看 permission 表里 id 1234 分别对应着增删改除四个权限,由此可知用户 admin 为管理员,拥有四个权限。其他用户的权限也是这么推算的我就不多赘述了。

基本架构搭建

基本架构是一个 Spring MVC 的工程,源码链接我放下在结尾。

你们拿到的工程打后大概是这样的:

在这里插入图片描述

在基本架构的基础上我们要添加一些配置,pom.xml 代码如下:

<?xml version=\"1.0\" encoding=\"UTF-8\"?><project xmlns=\"http://maven.apache.org/POM/4.0.0\"         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">    <modelVersion>4.0.0</modelVersion>    <groupId>org.example</groupId>    <artifactId>erp</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>war</packaging>    <dependencies>        <!-- 测试相关 -->        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.12</version>        </dependency>        <!-- springmvc -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>5.2.6.RELEASE</version>        </dependency>        <!-- servlet -->        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>4.0.0</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>javax.servlet.jsp</groupId>            <artifactId>jsp-api</artifactId>            <version>2.2</version>            <scope>provided</scope>        </dependency>        <!--文件上传-->        <dependency>            <groupId>commons-fileupload</groupId>            <artifactId>commons-fileupload</artifactId>            <version>1.4</version>        </dependency>        <!-- jackson -->        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-core</artifactId>            <version>2.11.2</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-annotations</artifactId>            <version>2.11.2</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>2.11.2</version>        </dependency>        <!-- mybatis 相关 -->        <dependency>            <groupId>org.mybatis</groupId>            <artifactId>mybatis</artifactId>            <version>3.5.2</version>        </dependency>        <!-- 数据库连接驱动 相关 -->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>8.0.21</version>        </dependency>        <!-- 提供了对 JDBC 操作的完整封装 -->        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jdbc</artifactId>            <version>5.2.6.RELEASE</version>        </dependency>        <!-- 织入 相关 -->        <dependency>            <groupId>org.aspectj</groupId>            <artifactId>aspectjweaver</artifactId>            <version>1.9.4</version>        </dependency>        <!-- spring,mybatis 整合包 -->        <dependency>            <groupId>org.mybatis</groupId>            <artifactId>mybatis-spring</artifactId>            <version>2.0.2</version>        </dependency>        <!-- 集成德鲁伊使用 -->        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid</artifactId>            <version>1.1.18</version>        </dependency>        <!--日志-->        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-api</artifactId>            <version>1.7.30</version>        </dependency>        <dependency>            <groupId>ch.qos.logback</groupId>            <artifactId>logback-classic</artifactId>            <version>1.2.3</version>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <version>1.18.12</version>        </dependency>        <dependency>            <groupId>com.github.pagehelper</groupId>            <artifactId>pagehelper</artifactId>            <version>5.2.0</version>        </dependency>        <dependency>            <groupId>org.thymeleaf</groupId>            <artifactId>thymeleaf</artifactId>            <version>3.0.12.RELEASE</version>        </dependency>        <dependency>            <groupId>org.thymeleaf</groupId>            <artifactId>thymeleaf-spring4</artifactId>            <version>3.0.12.RELEASE</version>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <version>3.1</version>                <configuration>                    <source>1.8</source> <!-- 源代码使用的 JDK 版本 -->                    <target>1.8</target> <!-- 需要生成的目标 class 文件的编译版本 -->                    <encoding>utf-8</encoding><!-- 字符集编码 -->                </configuration>            </plugin>        </plugins>        <resources>            <resource>                <directory>src/main/java</directory>                <includes>                    <include>**/*.properties</include>                    <include>**/*.xml</include>                </includes>                <filtering>false</filtering>            </resource>            <resource>                <directory>src/main/resources</directory>                <includes>                    <include>**/*.properties</include>                    <include>**/*.xml</include>                </includes>                <filtering>false</filtering>            </resource>        </resources>    </build></project>

如何生成代码

EasyCode 插件下载

直接去设置里搜索下载就好了,如果你的 IDEA 无法联网,就去官网下载然后离线安装,这个网上很多教程。

在这里插入图片描述

EasyCode 这个插件真的非常好用,直接省掉了大部分的基础代码,能更好的面向业务开发。

步骤

复制一下 url:

jdbc:mysql://localhost:3306/mysecurity?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

打开 Database:

在这里插入图片描述

输入账号密码和 URL,点击 TestConnection 测试链接,成功后保存:

在这里插入图片描述

在这里插入图片描述

选中这三张表右键,使用 EasyCode 生成代码,一直点 yes 就可以了。

在这里插入图片描述

在这里插入图片描述

这时工程目录就是这样了,controller、dao、entity、service、util 以及对应的 xml 文件统统一键生成,非常方便。

没学会 Spring Aop?带你手撸一套 RBAC 风格的 AOP 权限系统框架

这时候重启项目测试,找到 usercontrller.java:

package cn.itnanls.controller;import cn.itnanls.entity.User;import cn.itnanls.service.UserService;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;/** * (User)表控制层 * * @author binge * @since 2021-05-02 15:07:10 */@RestController@RequestMapping(\"user\")public class UserController {    /**     * 服务对象     */    @Resource    private UserService userService;    /**     * 通过主键查询单条数据     *     * @param id 主键     * @return 单条数据     */    @GetMapping(\"selectOne\")    public User selectOne(Integer id) {        return this.userService.queryById(id);    }}

去浏览器打开地址查询数据库数据:

http://localhost:8080/user/selectOne?id=1

得到数据如下,项目构建完成:

{\"id\":1,\"username\":\"admin\",\"password\":\"123\"}

在这里插入图片描述

构建完成项目源码:

https://pan.baidu.com/s/1QxP-0iAJ2_Xy7RKrkl4c0g

提取码:1jqp

基本 Web 页面实现

在 WEB-INF 路径下新建包 pages,加入以下三个页面,分别是首页、登录、登录后操作的页面。

index.html

<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head >    <meta charset=\"UTF-8\">    <title>itnanls</title></head><body><a th:href=\"@{/tologin}\" th:if=\"${#session.getAttribute(\'user\') eq null }\"> 登录 </a><!--1 页面点击--><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:add\")}\' >添加用户</a><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:delete\")}\'>删除用户</a><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:update\")}\'>修改用户</a><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:query\")}\'>查询用户</a></body></html>

login.html

<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head >    <meta charset=\"UTF-8\">    <title>itnanls</title>    <base th:href=\"@{/}\"></head><body><form>    <input name=\"username\" type=\"text\">    <input name=\"password\" type=\"password\">    <input id=\"btn\" type=\"button\" value=\"提交\"></form><script src=\"https://cdn.bootcdn.net/ajax/libs/jquery/2.2.4/jquery.js\"></script><script>    $(\"#btn\").click(function(){     $.post(\"login\",$(\"form\").serialize(),function (data) {     if(data.code === 200){         location.href = \"/\" ;     }else {         alert(data.msg)     }})})</script></body></html>

user.html

<!DOCTYPE html><html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\"><head >    <meta charset=\"UTF-8\">    <title>itnanls</title></head><body><!--提交表单--><form>    <input name=\"username\" type=\"text\">    <input name=\"password\" type=\"password\">    <input id=\"btn\" type=\"button\" value=\"提交\"></form><script src=\"https://cdn.bootcdn.net/ajax/libs/jquery/2.2.4/jquery.js\"></script><script>    $(\"#btn\").click(function(){        $.post(\"user\",$(\"form\").serialize(),function (data) {            if(data.code === 200){                location.href = \"/\" ;            }        })    })</script></body></html>

按钮级别的控制

需要注意的是,index.heml 里这几句代码:

<a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:add\")}\' >添加用户</a><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:delete\")}\'>删除用户</a><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:update\")}\'>修改用户</a><a th:href=\"@{/toUser}\" th:if=\'${ #session.getAttribute(\"permissions\") ne null && #lists.contains(#session.getAttribute(\"permissions\"),\"user:query\")}\'>查询用户</a>

它们的作用是从后台拿到登录人的 session,判断在不为空且拥有对应权限的时候才会显示按钮。在前台即可先完成判断,因为用户是没有删除添加用户的权限的,所以一些不能显示给普通用户。

页面跳转类

新建一个 PageJump.java,用于实现页面跳转:

package cn.itnanls.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpSession;/** * @author binge * @function 由于 UserController 的@RestController 不能走视图解析器,所以不能跳转页面 * 所以跳转页面相关统一放这里 */@Controllerpublic class PageJump {    @RequestMapping(\"/\")    public String toIndex(HttpSession session){        return \"index\";    }    @RequestMapping(\"/tologin\")    public String toLogin(){        return \"login\";    }    /**     * 请求处理,返回页面     * @return     */    @RequestMapping(\"/toUser\")    public String toUser(){        return \"user\";    }}

xml 文件修改

添加多表查询语句

在 PermissionDao.xml 里加入根据用户查询权限的功能:

 <!--根据用户查询权限-->    <select id=\"getPermissionsByUserName\" resultMap=\"PermissionMap\">         SELECT            p.id id,            p.permission_name permission_name        FROM            user u            LEFT JOIN user_role ur on u.id = ur.u_id            LEFT JOIN role r ON ur.r_id = r.id            LEFT JOIN role_permission rp ON rp.r_id = r.id            LEFT JOIN permission p ON rp.p_id = p.id        WHERE            u.username = #{username}    </select>

并且在 PermissionDao 接口里定义方法:

    /**     * 根据用户查询权限     * @param username     * @return     */    List<Permission> getPermissionsByUserName(String username);

在 PermissionServiceImpl.java 里实现方法:

 /**     * 根据用户查询权限     * @param username     * @return     */    @Override    public List<Permission> getPermissionsByUserName(String username) {        return permissionDao.getPermissionsByUserName(username);    }

RoleDao.xml 里也要添加,也要在对应接口和实现类里定义实现方法,方法同上不多赘述。

  <!--查询角色-->    <select id=\"getRolesByUserName\" resultMap=\"RoleMap\">        SELECT            r.id id,            r.role_name role_name        FROM            user u            LEFT JOIN user_role ur on u.id = ur.u_id            LEFT JOIN role r on ur.r_id = r.id        WHERE            u.username = #{username}    </select>

认证与鉴权

传统的判断权限

我们再回到 usercontrller,平常我们判断用户登录对简单常用的的是直接判断字符串是否相等,但这只适用于小打小闹,真正要判断成千上万的用户这是行不通的。

以下是使用 permissions 从数据库判断数据:

    /**     * 添加数据     */    @PostMapping()    public R userAdd(HttpSession session, User user) {        //传统的判断权限        List<String> permissions = ( List<String>)session.getAttribute(\"permissions\");        boolean contains = permissions.contains(\"user:add\");        if(! contains){            throw new NotHasPermissionsException(\"没有用户添加的权限\");        }        this.userService.insert(user);        return R.of(ResultCode.SUCCESS);    }

AOP 权限判断

新建包 care,定义一个注解 HasPermission:

package cn.itnanls.core.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * @author binge * @function @Target 元注解,表示是在方法上加 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface HasPermission {    String[] value();}

再定义一个实现类 PermissionAop:

package cn.itnanls.core.aop;import cn.itnanls.core.annotation.HasPermission;import cn.itnanls.core.exception.NotHasPermissionsException;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpSession;import java.util.List;import java.util.stream.Stream;/** * @author binge * @function WHAT */@Aspect@Componentpublic class PermissionAop {    /**     * 环绕通知     * @param joinPoint     * @return     */    @Around(\"@annotation(cn.itnanls.core.annotation.HasPermission)\")    public Object around(ProceedingJoinPoint joinPoint) {        //使用 ServletRequestAttributes 获取 session        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();        HttpSession session = sra.getRequest().getSession();        //拿到权限        List<String> permissions = (List<String>) session.getAttribute(\"permissions\");        //获取访问接口所需的权限        HasPermission annotation = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(HasPermission.class);        String[] needPermission = annotation.value();        //验证匹配 (allMatch:全部匹配 anyMatch:至少一个匹配)        boolean flag = Stream.of(needPermission).anyMatch(permissions::contains);        if (!flag) {            throw new NotHasPermissionsException(\"您没有用户添加的权限\");        }        Object obj = null;        try {            obj = joinPoint.proceed(joinPoint.getArgs());        } catch (Throwable throwable) {            throwable.printStackTrace();        }        System.out.println(\"添加成功\");        return obj;    }    /**     * 前置通知     * @param joinPoint     * @return     *///    @Before(\"@annotation(cn.itnanls.core.annotation.HasPermission)\")//    public void around(JoinPoint joinPoint) {////        //使用 ServletRequestAttributes 获取 session//        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();////        HttpSession session = sra.getRequest().getSession();////        //拿到权限//        List<String> permissions = (List<String>) session.getAttribute(\"permissions\");////        //获取访问接口所需的权限//        HasPermission annotation = joinPoint.getTarget().getClass().getAnnotation(HasPermission.class);//        String[] needPermission = annotation.value();////        //验证匹配 (allMatch:全部匹配 anyMatch:至少一个匹配)//        boolean flag = Stream.of(needPermission).allMatch(permissions::contains);////        if (!flag) {//            throw new NotHasPermissionsException(\"您没有用户添加的权限\");//        }//    }}

此时我们就可以使用 AOP 注解的形式实现鉴权了。

    @Delete(\"{id}\")    @HasPermission(\"user:delete\")    public R DeleteOne(@PathVariable Integer id) {        this.userService.deleteById(id);        return R.of(ResultCode.SUCCESS);    }    @PostMapping    @HasPermission(\"user:add\")    public R AddOne(HttpSession session,  User user) {        this.userService.insert(user);        return R.of(ResultCode.SUCCESS);    }

实现一个 Realm

定义 realm 接口:

package cn.itnanls.core;/** * @author binge * @function 认证授权的核心,负责从数据库拿数据比对 */public interface Realm {    /**     * 查询用户信息     * @param username     * @return     */    UserToken authentication(String username);    /**     * 查询权限信息     * @param username     * @return     */    PermissionInfo authorization( String username);}

实现接口,建立 UserRealm.java。

实现账号密码的初步判断和查询出对应的权限和角色,包装成 PermissionInfo。

package cn.itnanls.Security;import cn.itnanls.core.PermissionInfo;import cn.itnanls.core.Realm;import cn.itnanls.core.UserToken;import cn.itnanls.core.exception.UserNotFindException;import cn.itnanls.core.exception.UsernameIsEmptyException;import cn.itnanls.entity.Permission;import cn.itnanls.entity.Role;import cn.itnanls.entity.User;import cn.itnanls.service.PermissionService;import cn.itnanls.service.RoleService;import cn.itnanls.service.UserService;import com.github.pagehelper.util.StringUtil;import org.springframework.stereotype.Component;import javax.annotation.Resource;import java.util.List;import java.util.stream.Collectors;import java.util.stream.Stream;/** * @author binge * @function WHAT */@Componentpublic class UserRealm implements Realm {    @Resource    private UserService userService;    @Resource    private RoleService roleService;    @Resource    private PermissionService permissionService;    @Override    public UserToken authentication(String username) {        List<User> userByName = userService.findUserByName(username);        if(StringUtil.isEmpty(username)) {            throw new UsernameIsEmptyException(\"用户名为空\");        }       // 如果 userByName 里没有数据,说明用户不存在        if(userByName.isEmpty() || userByName.size() == 0){            throw new UserNotFindException(\"用户名未找到\");        }        return new UserToken(userByName.get(0).getUsername(),userByName.get(0).getPassword());    }    @Override    public PermissionInfo authorization(String username) {        //查询出所有的权限和角色,包装成 PermissionInfo        List<Permission> permissions = permissionService.getPermissionsByUserName(username);        List<String> permissionStr = permissions.stream().map(Permission::getPermissionName).collect(Collectors.toList());        List<Role> roles = roleService.getRolesByUserName(username);        List<String> rolesStr = roles.stream().map(Role::getRoleName).collect(Collectors.toList());        PermissionInfo permissionInfo = new PermissionInfo();        permissionInfo.setRoles(rolesStr);        permissionInfo.setPermission(permissionStr);        return permissionInfo;    }}

登录踢人的实现

原理:登录踢人的实现主要同过 session 来实现,session 是一个会话,每个浏览器与服务器的连接都会建立一个 session,这个 session 是唯一的标识。那么我们判断登录后先获取第一个登录者的 session,在第二个使用相同用户名登录的人销毁第一个人的 session,那么就可以实现登录踢人的效果了。

功能实现

新建 Subject.java:

package cn.itnanls.core;import cn.itnanls.core.exception.PasswordIsNotCorrectException;import cn.itnanls.core.exception.UserNotFindException;import cn.itnanls.core.exception.UsernameIsEmptyException;import cn.itnanls.entity.Permission;import cn.itnanls.entity.Role;import cn.itnanls.entity.User;import cn.itnanls.util.R;import cn.itnanls.util.ResultCode;import com.github.pagehelper.util.StringUtil;import org.springframework.stereotype.Component;import org.springframework.stereotype.Controller;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import sun.misc.Contended;import javax.annotation.PostConstruct;import javax.annotation.Resource;import javax.servlet.http.HttpSession;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.stream.Collectors;/** * @author binge * @function WHAT */@Componentpublic class Subject {    @Resource    private Realm realm;    /**     * 存放登录状态的 session     */    private static Map<String,HttpSession> ONLINE_SESSION = new HashMap<>(16);    private static Realm myRealm;    /**     * 当实例对象注入完成后会调用此方法,避免 myRealm 报空针     */    @PostConstruct    public void Init(){        myRealm = realm;    }    public static void login(String name,String pwd){        //对应户名进行验证        UserToken token = myRealm.authentication(name);        String username = token.getUsername();        /**         * 判断当前登录的 username 是否有 session,有就销毁         */        boolean UserNameIsExist = ONLINE_SESSION.containsKey(username);        if (UserNameIsExist){            ONLINE_SESSION.get(username).invalidate();        }        //找到用户,对比密码        if(! token.getPassword().equals(pwd)){            throw new PasswordIsNotCorrectException(\"密码错误\");        }        getSession().setAttribute(\"user\",token.getUsername());        getSession().setAttribute(\"roles\",myRealm.authorization(token.getUsername()).getRoles());        getSession().setAttribute(\"permissions\",myRealm.authorization(token.getUsername()).getPermission());        ONLINE_SESSION.put(token.getUsername(),getSession());    }    public static HttpSession getSession(){        //使用 ServletRequestAttributes 获取 session        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();        HttpSession session = sra.getRequest().getSession();        return session;    }    }

添加过滤器

过滤器用来判断请求的 URI 是否允许通过。

在 web.xml 里配置:

<!--配置拦截器-->    <filter>        <filter-name>LoginFilter</filter-name>        <filter-class>cn.itnanls.core.LoginFilter</filter-class><!--配置不被拦截的路径-->        <init-param>            <param-name>ignore</param-name>            <param-value>/,/index,/tologin,/login</param-value>        </init-param>    </filter>    <filter-mapping>        <filter-name>LoginFilter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>

新建 LoginFilter.java:

package cn.itnanls.core;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;/** * @author binge * @function WHAT */public class LoginFilter implements Filter {    public String[] ignoreUri;    @Override    public void init(FilterConfig filterConfig) throws ServletException {        String ignore = filterConfig.getInitParameter(\"ignore\");        ignoreUri = ignore.split(\",\");    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest)servletRequest;        HttpServletResponse response = (HttpServletResponse)servletResponse;        //判断请求的 URI 是否允许通过        boolean isIgnore = isIgnore(servletRequest);        if (isIgnore) {            filterChain.doFilter(servletRequest,servletResponse);        } else {            HttpSession session = request.getSession();            Object user = session.getAttribute(\"user\");            if(user == null){                response.sendRedirect(\"/\");            }else {                filterChain.doFilter(servletRequest,servletResponse);            }        }    }    public boolean isIgnore(ServletRequest request){        HttpServletRequest httpServletRequest = (HttpServletRequest)request;        for (String ignore : ignoreUri) {            if(httpServletRequest.getRequestURI().equals(ignore)){                return true;            }        }        return false;    }}

完整工程源码

链接:https://pan.baidu.com/s/1FUUY4AUho6P-imrD0rscpA

提取码:7lsv

小七学习网,助您升职加薪,遇问题可联系:客服微信【1601371900】 备注:来自网站

免责声明: 1、本站信息来自网络,版权争议与本站无关 2、本站所有主题由该帖子作者发表,该帖子作者与本站享有帖子相关版权 3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和本站的同意 4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责 5、用户所发布的一切软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。 6、您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 7、请支持正版软件、得到更好的正版服务。 8、如有侵权请立即告知本站(邮箱:1099252741@qq.com,备用微信:1099252741),本站将及时予与删除 9、本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章和视频仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。