Apache Shiro Framework 介绍
第一章:Shiro简介
1、1 Shiro框架简介
Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。
官网地址如下:http://shiro.apache.org/
Shiro的具体功能点如下:![屏幕快照 2017-08-25 下午4.24.25](屏幕快照 2017-08-25 下午4.24.25.png)
(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 实现来代表自定义的数据源。
第二章:Shiro之编码加密
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>
2.1、关于shiro加密的说明
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身份认证
用户身份验证,在应用程序中做身份以及角色的认证。一般都是使用用户名和密码作为认证的凭据。
在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 授权机制
在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 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
~~~
配置shiro.ini用户如下规则:
[users]是标志用户信息,规则如下:
用户名 = 密码,角色1,角色2,角色3……
分析数据结构:一个用户可以拥有多个角色
~~~ini
—————————————————————————–
Roles with assigned permissions
roleName = perm1, perm2, …, permN
—————————————————————————–
[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认证流程
在Shiro中Realm(域)可以访问特定的应用程序安全实体(如:用户、角色和权限)以确定身份验证和授权操作。
Realm通常和数据源有关系,例如关系型数据库、文件系统等,具有一对一的对应关系。Realm接口实现使用特定
数据源的API接口访问数据,也就是我们可以在数据库中直接定义 用户表 、 角色表和权限表等,我们可以使用JDBC、Hibernate、JPA的规范访问,Realm本质上是数据访问层的DAO。
由于每个应用程序不同,用户和角色等安全数据可以以多种方式进行表示。 Shiro尝试尽可能保持非侵入性的开发哲学 - 它不要求您实现或扩展任何用户,组或角色接口或类
大多数用户不会直接实现Realm接口,而是扩展其中一个子类AuthenticatingRealm或AuthorizingRealm,大大降低了从头开始实现Realm的工作量。
AuthenticatingRealm类的介绍
该类主要是实现了验证模块的信息,通过它可以实现一系列用户登录校验和角色判断等功能,通常该类是配合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 使用JdbcRealm进行验证
我们先来看看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);
}
}
第七章:使用原生的Java代码对Realm类进行校验和授权操作
在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整合以及实现等验证操作
Shiro提供了与Web集成环境的安全应用支持,
我们在Web工程下的web.xml配置简单的ShiroFilter来控制所有的URL请求,并且根据请求进行转发,
其通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,其是安全控制的入口点,其负责读取配置(如ini配置文件),然后判断URL是否需要登录/权限等工作。