Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。
官网地址如下:http://shiro.apache.org/
Shiro的具体功能点如下:
(1)身份认证/登录,验证用户是不是拥有相应的身份; (2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限; (3)会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的; (4)加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储; (5)Web支持,可以非常容易的集成到Web环境;
(6) Caching :缓存,存储用户登录之后的信息,拥有角色和权限不需要每次都去查
##### 1、2 Shiro主要有四个组件
SecurityManager典型的 Facade,Shiro 通过它对外提供安全管理的各种服务。
Authenticator对“Who are you ?”进行核实。通常涉及用户名和密码。这 个组件负责收集 principals 和 credentials,并将它们提交给应用系统。如果提交的 credentials 跟应用系统中提供的 credentials 吻合,就能够继续访问,否则需要重新提交 principals 和 credentials,或者直接终止访问。
Authorizer身 份份验证通过后,由这个组件对登录人员进行访问控制的筛查,比如“who can do what”, 或者“who can do which actions”。Shiro 采用“基于 Realm”的方法,即用户(又称 Subject)、用户组、角色和 permission 的聚合体。
Session Manager这个组件保证了异构客户端的访问,配置简单。它是基于 POJO/J2SE 的,不跟任何的客户端或者协议绑定。
同样的虚线框框圈着的是Shiro3大核心组件:
Subject :正与系统进行交互的人,或某一个第三方服务。所有 Subject 实例都被绑定到(且这是必须的)一个SecurityManager 上。 SecurityManager:Shiro 架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。 Realms :本质上是一个特定安全的 DAO。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC),INI 及属性文件等。可以定义自己 Realm 实现来代表自定义的数据源。
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>com.sudojava.shiro</groupId> <artifactId>shiro_java</artifactId> <version>1.0-SNAPSHOT</version> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.6.1</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>Shiro提供了base64和16进制字符串编码/解码的API支持,方便一些编码解码操作。Shiro内部的一些数据的存储/表示都使用了base64和16进制字符串,实现不同的加密算法如下:
产生随机的“盐值”算法
package com.sudojava.shiro.commons; import org.apache.shiro.SecurityUtils; import org.apache.shiro.crypto.RandomNumberGenerator; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; public class RandomNumberUtils { //随机数的代码生成器 private static RandomNumberGenerator generator; static { generator = new SecureRandomNumberGenerator(); } /** * 获取工具类 * @return */ public static ByteSource getByteSource(){ if (generator!=null){ return generator.nextBytes(); } return null; } }各种不同的加密算法如下:
package com.sudojava.shiro.cryptography; import com.sudojava.shiro.commons.RandomNumberUtils; import org.apache.shiro.crypto.hash.*; public class CryptoUtils { protected static Object getSalt() { return RandomNumberUtils.getByteSource().toHex(); } public static String cryptoObject(String source,String algorithm,int hashIterations) { switch (algorithm) { case "md5": { Md5Hash md5Hash = new Md5Hash(source,getSalt(),hashIterations); return md5Hash.toHex(); } case "md2": { Md2Hash md2Hash = new Md2Hash(source,getSalt(),hashIterations); return md2Hash.toHex(); } case "sha1": { Sha1Hash sha1Hash = new Sha1Hash(source,getSalt(),hashIterations); return sha1Hash.toHex(); } case "sha256": { Sha256Hash sha256Hash = new Sha256Hash(source,getSalt(),hashIterations); return sha256Hash.toHex(); } default: return null; } } }测试加密算法结果:
package com.sudojava.shiro.test; import com.sudojava.shiro.cryptography.CryptoUtils; import org.apache.shiro.crypto.hash.Md5Hash; import org.junit.Test; public class TestSimpleHash { @Test public void testMethod() { String username = "admin"; String result1 = CryptoUtils.cryptoObject(username, "md5", 4); System.out.println(result1); String result2 = CryptoUtils.cryptoObject(username, "md2", 4); System.out.println(result2); String result3 = CryptoUtils.cryptoObject(username, "sha1", 4); System.out.println(result3); } }用户身份验证,在应用程序中做身份以及角色的认证。一般都是使用用户名和密码作为认证的凭据。
在shiro中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。
第一步:
配置shiro.ini文件,采用硬编码的方式提供认证信息
# ============================================================================= # Tutorial INI configuration # # Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :) # ============================================================================= # ----------------------------------------------------------------------------- # Users and their (optional) assigned roles # username = password, role1, role2, ..., roleN # ----------------------------------------------------------------------------- [users] root = secret, admin guest = guest, guest presidentskroob = 12345, president darkhelmet = ludicrousspeed, darklord, schwartz lonestarr = vespa, goodguy, schwartz配置规则:
用户名 = 密码,角色1,角色2,角色3等
模拟用户登录情景
用户登录类:Authorization
package com.sudojava.shiro.authorization; import com.sudojava.shiro.domain.User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Authorization { private static final transient Logger log = LoggerFactory.getLogger(Authorization.class); private Factory<SecurityManager> factory; private SecurityManager manager; public Authorization(String shiro_ini) { //获取SecurityManager工厂,加载shiro.ini文件 factory = new IniSecurityManagerFactory(shiro_ini); //得到SecurityManager实例,并绑定给SecurityUtils manager = factory.getInstance(); } /** * 模拟用户登录过程 * * @param user * @return */ public boolean login(User user) { boolean flag = false; SecurityUtils.setSecurityManager(manager); //得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) Subject currentUser = SecurityUtils.getSubject(); if (!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword()); token.setRememberMe(true);//记住用户登录信息 try { currentUser.login(token);//执行登录操作,进行身份验证 flag = true; } catch (UnknownAccountException e) { log.info("未知用户账号异常"); } catch (IncorrectCredentialsException e) { log.info("凭证不正确"); } catch (ExcessiveAttemptsException e) { log.info("尝试认证次数多于系统指定次数"); } catch (AuthenticationException e) { //所有认证异常的父类 log.info("其他未知错误!"); } } currentUser.logout();//用户登出操作 return flag; } }测试用户登录结果:
package com.sudojava.shiro.authorization; import com.sudojava.shiro.domain.User; import org.junit.Before; import org.junit.Test; public class TestAuthorization { private Authorization authorization; @Before public void init(){ authorization = new Authorization("classpath:shiro.ini"); } @Test public void login(){ User user = new User(); user.setPassword("guest"); user.setUsername("guest"); boolean flag = authorization.login(user); if (flag){ System.out.println("登录成功"); }else{ System.out.println("登录失败"); } } }在shiro中规定了授权的基本对象包括:Subject、Role和Permission和访问资源等四个主体对象。
Subject:主体
主体,指的是访问应用的用户,一般通知User对象。
Resource:资源
通常是指工程下资源模块,也可以指定特定的业务。
Role:角色
角色代表主体的身份特征,一般一种角色下可以拥有多种主体,所以是一对多的关系,例如:超级管理员、经理、CTO等。
Permission:权限
即指定授予主体操作某一些资源的权力,比如,查看、删除和修改的权力等。
授权方式
Shiro授权方式分为三种:
1、Java代码实现
通过Subject对象实现
2、注解实现
通过@RequireXXX实现
3、在web页面上实现
通过实现
案例讲解
模拟用户登录之后,判断该用户是否拥有一定权限,如果该用户有指定的权限,那么可以执行删除或者修改功能
配置shiro.ini文件
# ============================================================================= # Tutorial INI configuration # # Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :) # ============================================================================= # ----------------------------------------------------------------------------- # Users and their (optional) assigned roles # username = password, role1, role2, ..., roleN # ----------------------------------------------------------------------------- [users] root = secret, admin guest = guest, guest presidentskroob = 12345, president darkhelmet = ludicrousspeed, darklord, schwartz lonestarr = vespa, goodguy, schwartz jack = 123456,admin # ----------------------------------------------------------------------------- # Roles with assigned permissions # roleName = perm1, perm2, ..., permN # ----------------------------------------------------------------------------- [roles] admin = user:create,user:update,user:delete schwartz = lightsaber:* goodguy = winnebago:drive:eagle5核心BasicRole类:
我们声明一个抽象类,指定登录方法为抽象的方法
package com.sudojava.shiro.role; import com.sudojava.shiro.authorization.Authorization; import com.sudojava.shiro.domain.User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class BasicRole<T> { private Factory<SecurityManager> factory; private SecurityManager manager; public BasicRole(String shiro_ini) { //获取SecurityManager工厂,加载shiro.ini文件 factory = new IniSecurityManagerFactory(shiro_ini); //得到SecurityManager实例,并绑定给SecurityUtils manager = factory.getInstance(); } public Factory<SecurityManager> getFactory() { return factory; } public void setFactory(Factory<SecurityManager> factory) { this.factory = factory; } public SecurityManager getManager() { return manager; } public void setManager(SecurityManager manager) { this.manager = manager; } public abstract boolean login(T t); }核心登录LoginRole类
package com.sudojava.shiro.role; import com.sudojava.shiro.authorization.Authorization; import com.sudojava.shiro.domain.User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoginRole extends BasicRole<User> { private static final transient Logger log = LoggerFactory.getLogger(Authorization.class); public LoginRole(String shiro_ini) { super(shiro_ini); } @Override public boolean login(User user) { SecurityUtils.setSecurityManager(getManager()); Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword()); token.setRememberMe(true); try { subject.login(token); if (subject.hasRole("admin")) { log.info("该用户拥有admin角色身份"); if (subject.isPermitted("user:delete")) { log.info("该用户具有删除选项功能"); subject.execute(new Runnable() { @Override public void run() { try { Thread.sleep(3000); log.info("执行删除操作"); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } return true; } catch (UnknownAccountException e) { log.info("未知用户"); } } return false; } }核心测试类
package com.sudojava.shiro.role; import com.sudojava.shiro.domain.User; import org.junit.Before; import org.junit.Test; public class TestRole { private LoginRole loginRole; @Before public void init(){ loginRole = new LoginRole("classpath:shiro.ini"); } @Test public void test(){ User user = new User(); user.setUsername("root"); user.setPassword("secret"); boolean flag = loginRole.login(user); System.out.println(flag); } }关于Shiro.ini配置文件的说明
~~~ini
[users] root = secret, admin guest = guest, guest presidentskroob = 12345, president darkhelmet = ludicrousspeed, darklord, schwartz lonestarr = vespa, goodguy, schwartz jack = 123456,admin ~~~
配置shiro.ini用户如下规则:
[users]是标志用户信息,规则如下:
用户名 = 密码,角色1,角色2,角色3……
分析数据结构:一个用户可以拥有多个角色
~~~ini
[roles]
admin = user:create,user:update,user:delete schwartz = lightsaber:* goodguy = winnebago:drive:eagle5
~~~
[roles] 是标志用户角色拥有什么权限,规则如下:
资源:用户操作
例如:
admin = user:view
admin 拥有user的查看权限
admin = user:create,user:update,user:delete
admin 拥有user的创建和修改、删除权限
admin =user:*
admin 拥有user下的所有权限
admin = system:user :create, delete,view,find
admin 拥有system:user 下的CRUD功能
在Shiro中Realm(域)可以访问特定的应用程序安全实体(如:用户、角色和权限)以确定身份验证和授权操作。
Realm通常和数据源有关系,例如关系型数据库、文件系统等,具有一对一的对应关系。Realm接口实现使用特定
数据源的API接口访问数据,也就是我们可以在数据库中直接定义 用户表 、 角色表和权限表等,我们可以使用JDBC、Hibernate、JPA的规范访问,Realm本质上是数据访问层的DAO。
由于每个应用程序不同,用户和角色等安全数据可以以多种方式进行表示。 Shiro尝试尽可能保持非侵入性的开发哲学 - 它不要求您实现或扩展任何用户,组或角色接口或类大多数用户不会直接实现Realm接口,而是扩展其中一个子类AuthenticatingRealm或AuthorizingRealm,大大降低了从头开始实现Realm的工作量。
该类主要是实现了验证模块的信息,通过它可以实现一系列用户登录校验和角色判断等功能,通常该类是配合CacheManager一起使用的,后面我们在详细介绍。
代码示例
第一步配置shiro_realm.ini文件
[main] #声明一个realm myRealm1 = com.sudojava.shiro.realm.LoginRealm #指定securityManager的realms实现 securityManager.realms=$myRealm1第二步继承AuthenticatingRealm类
package com.sudojava.shiro.realm; import org.apache.shiro.authc.*; import org.apache.shiro.realm.AuthenticatingRealm; /** * 可以使用Realm获取数据库的基本信息, * 包括User表、权限表、和角色表 */ public class LoginRealm extends AuthenticatingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //取出用户名和密码 String username = token.getPrincipal().toString(); String password = new String((char[]) token.getCredentials()); if(!"zhang".equals(username)) { throw new UnknownAccountException(); //如果用户名错误 } if(!"123".equals(password)) { throw new IncorrectCredentialsException(); //如果密码错误 } return new SimpleAuthenticationInfo(username,password,getName()); } }备注:采用硬编码的方式,模拟数据库的账号为:zhang 密码为:123
第三步Login类的实现
package com.sudojava.shiro.realm; import com.sudojava.shiro.authorization.Authorization; import com.sudojava.shiro.domain.User; import com.sudojava.shiro.role.BasicRole; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Login extends BasicRole<User> { private static final Logger log = LoggerFactory.getLogger(Authorization.class); public Login(String shiro_ini) { super(shiro_ini); } @Override public boolean login(User user) { SecurityUtils.setSecurityManager(getManager()); Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()){ UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword()); token.setRememberMe(true); try { subject.login(token); return true; }catch (UnknownAccountException e){ log.info("账号不匹配"); } } return false; } }第四步测试类的实现
package com.sudojava.shiro.realm; import com.sudojava.shiro.domain.User; import org.junit.Before; import org.junit.Test; public class TestRealm { private Login login; @Before public void init(){ login = new Login("classpath:shiro_realm.ini"); } @Test public void login(){ User user = new User(); user.setUsername("zhang"); user.setPassword("123"); boolean flag = login.login(user); System.out.println(flag); } }我们先来看看Shiro的认证流程
从上面的继承关系图,我们可以看出来一般都是继承AuthorizingRealm就可以了,自带缓存的功能。
Shiro Realm主要默认实现:
org.apache.shiro.realm.text.IniRealm: 通过ini文件配置进行验证,
[users]部分指定用户名/密码及其角色;
[roles]部分指定角色即权限信息;
org.apache.shiro.realm.text.PropertiesRealm: user.username=password,role1,role2 标志用户名密码和角色 role.role1=permission1,permission2 标志角色和权限信息
org.apache.shiro.realm.jdbc.JdbcRealm:
我们可以通过创建对应的表,使用sql语句查询出来对应的信息:
获取用户密码:“select“select password from users where username = ?”, 获取用户密码及盐:“select password, password_salt from users where username = ?” 获取用户角色:“select role_name from user_roles where username = ?” 获取角色对应的权限信息:“select permission from roles_permissions where role_name = ?” 也可以调用相应的api进行自定义sql。
使用JdbcRealm进行验证示例步骤:
第一步:创建shiro-jdbc-realm.ini文件
[main] # 配置JDBC数据库连接 dataSource=com.alibaba.druid.pool.DruidDataSource dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql://localhost:3306/shiro dataSource.username=root dataSource.password=root # JdbcRealm jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm # 允许授权查询 jdbcRealm.permissionsLookupEnabled = true jdbcRealm.dataSource=$dataSource #编写查询数据库的SQL语句 jdbcRealm.authenticationQuery = select password from users where username = ? jdbcRealm.userRolesQuery = select a.role_name from user_roles a,users b where a.users_id = b.id and b.username = ? jdbcRealm.permissionsQuery =select a.permission FROM roles_permissions a,user_roles b where b.role_id = a.user_roles_role_id and b.role_name = ? securityManager.realms=$jdbcRealm第二步:在pom.xml文件中添加DataSource,使用的是阿里巴巴的数据驱动
<!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.0</version> </dependency> <!-- 添加mysql数据驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency>第三步:创建用户表users、user_roles、roles_permissions表
~~~mysql CREATE TABLE IF NOT EXISTS shiro.users ( id INT NOT NULL, username VARCHAR(100) NULL, password VARCHAR(100) NULL, password_salt VARCHAR(100) NULL, PRIMARY KEY (id)) ENGINE = InnoDB
CREATE TABLE IF NOT EXISTS shiro.user_roles ( role_id INT NOT NULL, role_name VARCHAR(100) NULL, users_id INT NOT NULL, PRIMARY KEY (role_id), INDEX fk_user_roles_users1_idx (users_id ASC), CONSTRAINT fk_user_roles_users1 FOREIGN KEY (users_id) REFERENCES shiro.users (id) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB
CREATE TABLE IF NOT EXISTS shiro.roles_permissions ( permission VARCHAR(100) NULL, p_id INT NOT NULL, user_roles_role_id INT NOT NULL, PRIMARY KEY (p_id), INDEX fk_roles_permissions_user_roles1_idx (user_roles_role_id ASC), CONSTRAINT fk_roles_permissions_user_roles1 FOREIGN KEY (user_roles_role_id) REFERENCES shiro.user_roles (role_id) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB ~~~
备注:请校验数据库的名称。
第四步:创建JDBCRealm相关的类
package com.sudojava.shiro.jdbcrealm; import com.sudojava.shiro.authorization.Authorization; import com.sudojava.shiro.domain.User; import com.sudojava.shiro.basic.BasicShiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.Permission; import org.apache.shiro.authz.permission.WildcardPermission; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LoginForJdbcRealm extends BasicShiro<User> { private static final Logger log = LoggerFactory.getLogger(Authorization.class); public LoginForJdbcRealm(String shiro_ini) { super(shiro_ini); } @Override public boolean login(User user) { SecurityUtils.setSecurityManager(getManager()); Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()){ UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword()); try { subject.login(token); token.setRememberMe(true); if (subject.hasRole("manager")){ log.info("该用户拥有 manager 角色"); subject.checkPermission("delete"); if (subject.isPermitted("delete")){ log.info("该用户具有删除的权限"); subject.execute(new Runnable() { @Override public void run() { try { Thread.sleep(3000); log.info("删除资源中......"); } catch (Exception e) { e.printStackTrace(); } } }); } } return true; } catch (UnknownAccountException e) { log.info(e.getMessage()+"账号名有误"); } } return false; } }模拟用户登录操作
package com.sudojava.shiro.jdbc; import com.sudojava.shiro.domain.User; import com.sudojava.shiro.jdbcrealm.LoginForJdbcRealm; import org.junit.Before; import org.junit.Test; public class TestForJdbc { private LoginForJdbcRealm realm; @Before public void setup(){ realm = new LoginForJdbcRealm("classpath:shiro-jdbc-realm.ini"); } @Test public void login(){ User user = new User(); user.setPassword("123"); user.setUsername("admin"); boolean flag = realm.login(user); System.out.println(flag); } }在shiro中我们可以采用Java代码对JdbcRealm进行封装,直接在登录模块进行校验操作。
手动封装Java类
package com.sudojava.shiro.nativeRealm; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.realm.jdbc.JdbcRealm; public class RealmDataSource { private MysqlDataSource dataSource; private DefaultSecurityManager securityManager; private JdbcRealm jdbcRealm; public RealmDataSource() { try { dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("root"); dataSource.setServerName("localhost"); dataSource.setLoginTimeout(2); dataSource.setUrl("jdbc:mysql://localhost:3306/shiro"); jdbcRealm = new JdbcRealm(); jdbcRealm.setDataSource(dataSource); jdbcRealm.setPermissionsLookupEnabled(true); //验证 String authentication_sql = "select password from users where username = ?"; jdbcRealm.setAuthenticationQuery(authentication_sql); //验证角色 String user_roles_sql = "select a.role_name from user_roles a,users b where a.users_id = b.id and b.username = ?"; jdbcRealm.setUserRolesQuery(user_roles_sql); String permission_sql = "select a.permission FROM roles_permissions a,user_roles b where b.role_id = a.user_roles_role_id and b.role_name = ?"; jdbcRealm.setPermissionsQuery(permission_sql); securityManager = new DefaultSecurityManager(jdbcRealm); } catch (Exception e) { e.printStackTrace(); } } public DefaultSecurityManager getSecurityManager() { return securityManager; } /** * @return */ public MysqlDataSource getDataSource() { if (dataSource != null) { return dataSource; } return null; } }模拟用户登录操作
package com.sudojava.shiro.nativeRealm; import com.sudojava.shiro.authorization.Authorization; import com.sudojava.shiro.domain.User; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NativeLogin { private static final Logger log = LoggerFactory.getLogger(Authorization.class); private RealmDataSource dataSource; public NativeLogin() { dataSource = new RealmDataSource(); } public boolean login(User user) { boolean flag = false; SecurityUtils.setSecurityManager(dataSource.getSecurityManager()); Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword()); token.setRememberMe(true); try { subject.login(token); System.out.println("login successfully"); flag = true; if (subject.hasRole("manager")){ log.info("该用户拥有 manager 角色"); subject.checkPermission("delete"); if (subject.isPermitted("delete")){ log.info("该用户具有删除的权限"); subject.execute(new Runnable() { @Override public void run() { try { Thread.sleep(3000); log.info("删除资源中......"); } catch (Exception e) { e.printStackTrace(); } } }); } } } catch (UnknownAccountException e) { e.printStackTrace(); } } return flag; } }测试Java类
package com.sudojava.shiro.nativerealm; import com.sudojava.shiro.domain.User; import com.sudojava.shiro.nativeRealm.NativeLogin; import org.junit.Before; import org.junit.Test; public class TestNative { private NativeLogin nativeLogin; @Before public void setup(){ nativeLogin = new NativeLogin(); } @Test public void loginUser(){ User user = new User(); user.setPassword("123"); user.setUsername("admin"); boolean flag = nativeLogin.login(user); System.out.println(flag); } }Shiro提供了与Web集成环境的安全应用支持,
我们在Web工程下的web.xml配置简单的ShiroFilter来控制所有的URL请求,并且根据请求进行转发,
其通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,其是安全控制的入口点,其负责读取配置(如ini配置文件),然后判断URL是否需要登录/权限等工作。
