Java Apache Shiro安全框架快速开发详解流程

Java Apache Shiro安全框架快速开发详解流程

什么是Apache Shiro

Apache Shiro是一个跨应用程序、支持单点登录、支持身份验证和访问控制框架,可以解决应用程序的安全问题。
Shiro的核心是将应用程序的用户身份、安全验证、访问控制等功能组合起来实现一个完整的安全框架。
使用Shiro开发的应用程序能够快速、安全地集成身份验证、安全验证、单点登录等功能。

Shiro框架的组件

Shiro由以下组件组成:

  • Subject:应用程序中的用户抽象对象,封装了与用户相关的对象和操作。

  • SecurityManager:Shiro框架的核心,用户管理、安全认证、访问控制等功能都与SecurityManager相关。

  • Authenticator:用于验证用户身份,提供不同的验证策略,如密码验证、指纹验证等。

  • Authorizer:用于授权用户访问资源,提供多种授权策略。

  • Realm:Shiro对数据源的抽象,用于获取用户身份认证信息和访问控制数据,可以将用户信息存储在数据库、LDAP或其他数据源中。

  • SessionManager:会话管理器,负责管理用户会话,可以定制不同的会话管理策略,如Cookie会话管理、URL会话跟踪等。

Shiro集成框架

在Java开发中,Shiro常常集成在一些框架中,如Spring、Struts等,通过集成Shiro,可以轻松地实现应用程序的安全模块。

下面就以Spring集成Shiro框架为例,介绍Shiro框架的快速开发流程。

快速搭建Shiro框架

在Spring应用程序中集成Shiro框架,通常需要完成以下步骤:

  1. 添加Shiro依赖:
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>1.7.0</version>
</dependency>
  1. 配置Shiro相关组件:
<!-- 配置SecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="myRealm" />
</bean>

<!-- 配置Realm -->
<bean id="myRealm" class="xx.xx.xxx.MyRealm">
    <!-- 配置数据源 -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 配置Shiro过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <!-- 配置过滤规则 -->
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
            /logout = logout
            /** = authc
        </value>
    </property>
    <!-- 配置登录URL -->
    <property name="loginUrl" value="/login.jsp" />
    <!-- 配置成功登录后的URL -->
    <property name="successUrl" value="/index.jsp" />
    <!-- 配置未登录时的URL -->
    <property name="unauthorizedUrl" value="/unauthorized.jsp" />
</bean>
  1. 编写自定义Realm:
public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 获取当前用户的角色和权限信息,并添加到authorizationInfo中
        User user = (User)principals.getPrimaryPrincipal();
        Set<String> roleNames = userService.findRoleNamesByUsername(user.getUsername());
        authorizationInfo.setRoles(roleNames);
        Set<String> permissions = userService.findPermissionsByUsername(user.getUsername());
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        // 根据用户名和密码查询用户信息
        User user = userService.findByUsernameAndPassword(upToken.getUsername(), new String(upToken.getPassword()));
        if (user != null) {
            // 如果用户存在返回一个身份认证信息
            AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
            return authenticationInfo;
        }
        throw new UnknownAccountException("用户名或密码错误");
    }
}

以上代码中,MyRealm类中的doGetAuthenticationInfo方法用于根据用户名和密码查询用户信息,如果找到用户信息则返回一个身份认证信息。
doGetAuthorizationInfo方法用于根据PrincipalCollection对象获取当前用户的角色和权限信息,并添加到相应的授权信息中。

  1. 使用Shiro相关API实现身份验证和访问控制:
// 获取Subject对象
Subject subject = SecurityUtils.getSubject();
// 构造UsernamePasswordToken对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
    // 身份验证
    subject.login(token);
} catch (AuthenticationException e) {
    // 身份验证失败
}
if (subject.isAuthenticated()) {
    // 访问控制
    if (subject.hasRole("admin")) {
        // admin用户的操作
    }
    if (subject.isPermitted("user:create")) {
        // 创建用户操作
    }
}

以上代码中,使用SecurityUtils.getSubject()方法获取当前用户对象,使用UsernamePasswordToken构造方法构造身份认证信息。
通过subject.login(token)方法进行身份验证,subject.hasRole()和subject.isPermitted()方法进行访问控制。

示例1:使用Shiro实现用户登录认证

  1. 创建login.jsp文件,并添加一个简单的表单用于用户登录:
<form action="/login" method="post">
    <input type="text" name="username" placeholder="用户名">
    <input type="password" name="password" placeholder="密码">
    <input type="submit" value="登录">
</form>
  1. 在Spring应用程序中配置Shiro相关组件,并添加用户登录控制器:
@Controller
public class LoginController {
    @RequestMapping("/login")
    public String login(HttpServletRequest request) {
        // 获取Subject对象
        Subject subject = SecurityUtils.getSubject();
        // 获取请求参数
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        // 构造UsernamePasswordToken对象
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            // 身份验证
            subject.login(token);
            // 身份验证成功跳转到首页
            return "redirect:/index";
        } catch (AuthenticationException e) {
            // 身份验证失败跳转到登录页面,显示错误信息
            request.setAttribute("error", "用户名或密码错误");
            return "login";
        }
    }
}
  1. 在MyRealm类中实现doGetAuthenticationInfo方法用于根据用户名和密码查询用户信息:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
    // 根据用户名和密码查询用户信息
    User user = userService.findByUsernameAndPassword(upToken.getUsername(), new String(upToken.getPassword()));
    if (user != null) {
        // 如果用户存在返回一个身份认证信息
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        return authenticationInfo;
    }
    throw new UnknownAccountException("用户名或密码错误");
}
  1. 运行应用程序并访问/login,输入用户名和密码进行登录认证。

示例2:使用Shiro实现角色授权和资源保护

  1. 在数据库中创建用户、角色和权限三张表,并添加测试数据:
CREATE TABLE `user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL DEFAULT '',
  `password` varchar(32) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `role` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `permission` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `user` (`id`, `username`, `password`) VALUES
    (1, 'admin', 'admin'),
    (2, 'user', 'user');

INSERT INTO `role` (`id`, `name`) VALUES
    (1, 'admin'),
    (2, 'user');

INSERT INTO `permission` (`id`, `name`) VALUES
    (1, 'user:read'),
    (2, 'user:create'),
    (3, 'user:update'),
    (4, 'user:delete');
  1. 在MyRealm类中实现doGetAuthorizationInfo方法,使用userService.findRoleNamesByUsername()和userService.findPermissionsByUsername()方法查询当前用户的角色和权限信息,并添加到authorizationInfo对象中:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    User user = (User)principals.getPrimaryPrincipal();
    Set<String> roleNames = userService.findRoleNamesByUsername(user.getUsername());
    authorizationInfo.setRoles(roleNames);
    Set<String> permissions = userService.findPermissionsByUsername(user.getUsername());
    authorizationInfo.setStringPermissions(permissions);
    return authorizationInfo;
}
  1. 在Spring应用程序中配置Shiro相关组件,并添加用户列表控制器:
@Controller
public class UserController {
    @RequestMapping("/users")
    public String list() {
        // 访问控制,如果用户没有"admin"角色或"users:view"权限,则禁止访问
        Subject subject = SecurityUtils.getSubject();
        if (!subject.hasRole("admin") && !subject.isPermitted("users:view")) {
            throw new UnauthorizedException("没有访问权限");
        }
        // 查询用户列表,并返回到users.jsp页面
        List<User> userList = userService.findAll();
        return "users";
    }
}
  1. 在Spring应用程序中修改Shiro过滤器,使用perms和roles配置用户访问控制规则:
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="myRealm" />
    <property name="sessionManager" ref="sessionManager" />
</bean>

<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
    <property name="globalSessionTimeout" value="3600000" />
    <property name="deleteInvalidSessions" value="true" />
    <property name="sessionDAO" ref="sessionDAO" />
</bean>

<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
    <property name="cacheManager" ref="cacheManager" />
</bean>

<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
</bean>

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
            /logout = logout
            /users = authc, perms[users:view], roles[admin]
            /** = authc
        </value>
    </property>
    <property name="loginUrl" value="/login.jsp" />
    <property name="successUrl" value="/index.jsp" />
    <property name="unauthorizedUrl" value="/unauthorized.jsp" />
</bean>
  1. 运行应用程序并访问/users,如果当前用户没有"admin"角色或者"users:view"权限,则禁止访问;如果有访问权限,则显示用户列表。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java Apache Shiro安全框架快速开发详解流程 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • 面试官问你redis是单线程还是多线程该怎么回答?

    近乎所有与Java相关的面试都会问到缓存的问题,基础一点的会问到redis数据格式、什么是“热数据和冷数据”,复杂一点的会问到缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题,这些看似不常见的概念,都与我们的缓存服务器相关,一般常用的缓存服务器有Redis、Memcached等,就redis单线程,这篇文章做一个简单介绍 Redis采用的是基于内存的采…

    Redis 2023年4月12日
    00
  • Redis Geo: Redis新增位置查询功能

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/144.html   移动互联网增进了人与人之间的联系,其中基于位置信息的服务(Location Based Service,LBS)起到很重要的促进作用。在移动互联网的大环境下,每个手机都变成了一个位置追踪设备,为人们提供了非常丰富的…

    Redis 2023年4月13日
    00
  • SQL – 别名

    SQL-别名的完整攻略 在SQL中,别名(Alias)是给一个表或一个列起一个别名,以便于提高查询语句的可读性。下面介绍SQL别名的具体用法及实例。 用法 SQL别名的使用方法为,使用AS关键字来为表或列起一个别名。语法如下: SELECT column_name AS alias_name FROM table_name; 实例 实例一 现有一张订单表,需…

    database 2023年3月27日
    00
  • SQL Server字符串切割函数

    下面是关于SQL Server字符串切割函数的完整攻略。 什么是SQL Server字符串切割函数 SQL Server字符串切割函数指的是用于将一个字符串拆分成多个子字符串的函数。 SQL Server字符串切割函数的类型 SQL Server字符串切割函数有以下两种类型: 内置字符串切割函数:包括SUBSTRING函数和CHARINDEX函数。SUBST…

    database 2023年5月21日
    00
  • 如何使用Python实现分页查询数据库数据?

    以下是使用Python实现分页查询数据库数据的完整攻略。 分页查询简介 分页查询是指将大量数据分成多个页面进行查询,以便好管理和展示数据。在Python中,可以使用pymysql库实现分查询数据库数据。 步骤1:连接到数据库 在Python,可以使用pymysql库连接到MySQL数据库。以下是连接到MySQL数据库的本语法: import pymysql …

    python 2023年5月12日
    00
  • AnzoGraph和MongoDB的区别

    AnzoGraph和MongoDB是两种不同类型的数据库管理系统,它们在数据存储、查询、处理等方面有很大差异。 首先,AnzoGraph是一种图数据库,广泛应用于语义网、知识图谱等领域,能够处理大量的图数据。MongoDB是一种文档型数据库,数据以文档的形式存储。 其次,在数据存储方面,AnzoGraph采用的是三元组模型,即以主语、谓语、宾语的形式存储数据…

    database 2023年3月27日
    00
  • Mysql数据库性能优化之子查询

    Mysql数据库性能优化之子查询 什么是子查询? 以一个完整的 SELECT 语句为基础,嵌套一个子 SELECT 语句,这个子 SELECT 语句被用作基础 SELECT 语句中的一个条件或表达式,就叫做子查询。 子查询可以出现在 SELECT、FROM、WHERE、HAVING、SET 和 VALUES 等子句中,常见的有 exists、IN 和子查询作…

    database 2023年5月19日
    00
  • Python操作MySQL的一个报错:IndexError: out of range

    # -*- coding: utf-8 -*-import sysimport MySQLdbtry: conn=MySQLdb.connect(host=”localhost”,user=”root”,passwd=”xxx”)except Exception,e:print e sys.exit()#获取操作游标cursor=conn.cursor()#…

    MySQL 2023年4月13日
    00
合作推广
合作推广
分享本页
返回顶部