1、核心依赖

        <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>

2、认证流程:创建SecurityManager-->主体提交认证-->SecurityMananger认证-->Authentictor认证-->Realm验证(从subject.login(token)开始跟踪源码可以验证(idea下ctrl+alt+b跟踪源码)),单元测试代码如下:

package com.example.demo_mg;

import junit.framework.Assert;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class DemoMgApplicationTests { SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); @Before
public void before() {
simpleAccountRealm.addAccount("wzs", "123456");
} @Test
public void contextLoads() {
} @Test
public void test() {
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wzs", "123456");
subject.login(token);
Assert.assertEquals(subject.isAuthenticated(), true);
subject.logout();
Assert.assertEquals(subject.isAuthenticated(), true);
}
}

账号错误UnknownAccountException,密码错误IncorrectCredentialsException。

从subject.login(token);点击ctrl+alt+b跟踪源码到DelegatingSubject的login方法,调用Subject subject = this.securityManager.login(this, token);,继续跟踪到DefaultSecurityManager的login方法,调用info = this.authenticate(token);,继续跟踪到AuthenticatingSecurityManager的authenticate方法,调用this.authenticator.authenticate(token);,继续跟踪到AbstractAuthenticator的authenticate方法,调用info = this.doAuthenticate(token);,继续跟踪到ModularRealmAuthenticator的doAuthenticate方法,可以看到如下认证代码,通过realm实现认证:

    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
this.assertRealmsConfigured();
Collection<Realm> realms = this.getRealms();
return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}

3、授权流程:创建SecurityManager-->主体授权-->SecurityManager授权-->Authorizer授权-->Realm获取角色权限数据(数据库|缓存等):

package com.example.demo_mg;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class DemoMgApplicationTests { SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); @Before
public void before() {
simpleAccountRealm.addAccount("wzs", "123456", "admin", "user");
} @Test
public void contextLoads() {
} @Test
public void test() {
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wzs", "123456");
subject.login(token);
//授权
subject.checkRole("admin");
subject.checkRoles("admin", "user");
subject.logout();
}
}

角色不存在UnauthorizedException

从subject.checkRoles("admin", "user");点击ctrl+alt+b跟踪源码到DelegatingSubject的checkRoles方法,调用this.securityManager.checkRoles(this.getPrincipals(), roleIdentifiers);,继续跟踪到AuthorizingSecurityManager的checkRoles方法,调用this.authorizer.checkRoles(principals, roles);,继续跟踪到ModularRealmAuthorizer的checkRoles方法,遍历roles调用this.checkRole(principals, role);,继续跟踪到checkRole方法,调用this.hasRole(principals, role),继续跟踪到hasRole方法,可见如下代码,通过realm授权:

    public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
this.assertRealmsConfigured();
Iterator var3 = this.getRealms().iterator(); Realm realm;
do {
if (!var3.hasNext()) {
return false;
} realm = (Realm)var3.next();
} while(!(realm instanceof Authorizer) || !((Authorizer)realm).hasRole(principals, roleIdentifier)); return true;
}

4、测试内置的IniRealm:

在src/test下新建Direcotry,名称resources,右键Mark Direcotry As-->Test Sources Root,在下面新建user.ini文件,内容:

[users]
wzs=123456,admin
[roles]
admin=user:delete,user:update

单元测试代码:

package com.example.demo_mg;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class DemoMgApplicationTests { @Test
public void contextLoads() {
} @Test
public void test() {
IniRealm iniRealm = new IniRealm("classpath:user.ini");
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
//主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wzs", "123456");
subject.login(token);
//授权
subject.checkRole("admin");
subject.checkPermissions("user:delete", "user:update");
subject.checkPermission("user:insert");
subject.logout();
}
}

权限不存在UnauthorizedException

5、测试内置的JdbcRealm:

1)安装mysql8.0

2)按照JdbcRealm的源码的SQL建表:

CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`password_salt` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; CREATE TABLE `user_roles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`role_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; CREATE TABLE `roles_permissions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_name` varchar(255) DEFAULT NULL,
`permission` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

3)添加测试数据:

insert into users (username,password) values ('wzs','123456');
insert into user_roles (username,role_name) values ('wzs','admin');
insert into roles_permissions (role_name,permission) values ('admin','user:delete');

4)依赖

        <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>

5)单元测试代码:

package com.example.demo_mg;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class DemoMgApplicationTests {
DruidDataSource dataSource = new DruidDataSource();
{
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://129.204.58.30:3306/shiro");
dataSource.setUsername("user");
dataSource.setPassword("*********");
} @Test
public void contextLoads() {
} @Test
public void test() {
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
jdbcRealm.setPermissionsLookupEnabled(true);//为了查询权限表,要开启权限查询
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
//主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wzs", "123456");
subject.login(token);
//授权
System.out.println(subject.isAuthenticated());
System.out.println(subject.hasRole("admin"));
System.out.println(subject.isPermitted("user:delete"));
subject.logout();
}
}

6)若自定义用户、角色、权限三张表,需要自定义查询用户、角色、权限的SQL:

参考JdbcRealm的查询语句:
protected String authenticationQuery = "select password from users where username = ?";
protected String userRolesQuery = "select role_name from user_roles where username = ?";
protected String permissionsQuery = "select permission from roles_permissions where role_name = ?"; 设置自定义查询语句到jdbcRealm对象:
jdbcRealm.setAuthenticationQuery("自定义查询用户sql");
jdbcRealm.setUserRolesQuery("自定义查询角色sql");
jdbcRealm.setPermissionsQuery("自定义查询权限sql");

6、自定义Realm,参考JdbcRealm继承抽象的AuthorizingRealm类并实现其抽象方法,认证doGetAuthenticationInfo,授权doGetAuthenticationInfo,然后单元测试授权和认证:

package com.example.demo_mg.realm;

import org.apache.commons.collections.map.HashedMap;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection; import java.util.*; public class TestRealm extends AuthorizingRealm {
//模拟users、user_roles、roles_permissions三张表的查询,实际应用需要查询数据库或缓存
Map<String, String> users = new HashMap<>();
Map<String, Set<String>> user_roles = new HashedMap();
Map<String, Set<String>> roles_permissions = new HashedMap(); {
users.put("wzs", "123456");
user_roles.put("wzs", new HashSet<>(Arrays.asList("admin", "test")));
roles_permissions.put("admin", new HashSet<>(Arrays.asList("user:delete", "user:update")));
super.setName("TestRealm"); //设置Realm名称,可选
} @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从认证信息获取用户名
String username = (String)principalCollection.getPrimaryPrincipal();
//从数据库或缓存中获取角色、权限数据
Set<String> roles = user_roles.get(username);
Set<String> permissions = new HashSet<>();
for (String role : roles) {
Set<String> set;
if((set = roles_permissions.get(role)) != null) {
permissions.addAll(set);
}
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从主题传过来的认证信息中,获得用户名
String username = (String)authenticationToken.getPrincipal();
//通过用户名从数据库中获取凭证
String password = users.get(username);
if(password != null) {
return new SimpleAuthenticationInfo(username, password, super.getName());
}
return null;
}
}

单元测试:

package com.example.demo_mg;

import com.example.demo_mg.realm.TestRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class DemoMgApplicationTests {
@Test
public void contextLoads() {
} @Test
public void test() {
TestRealm testRealm = new TestRealm();
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(testRealm);
//主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wzs", "123456");
subject.login(token);
//授权
System.out.println(subject.isAuthenticated());
System.out.println(subject.hasRole("admin"));
System.out.println(subject.isPermitted("user:delete"));
subject.logout();
}
}

7、密码加密及加盐:

在6的基础上调整代码:

package com.example.demo_mg.realm;

import org.apache.commons.collections.map.HashedMap;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource; import java.util.*; public class TestRealm extends AuthorizingRealm {
//模拟users、user_roles、roles_permissions三张表的查询,实际应用需要查询数据库或缓存
Map<String, String> users = new HashMap<>();
Map<String, Set<String>> user_roles = new HashedMap();
Map<String, Set<String>> roles_permissions = new HashedMap();
String salt = UUID.randomUUID().toString().replaceAll("-",""); {
//不加盐(与认证对应)
// users.put("wzs", new Md5Hash("123456",null, 2).toString());
//加盐
users.put("wzs", new Md5Hash("123456",salt, 2).toString());
user_roles.put("wzs", new HashSet<>(Arrays.asList("admin", "test")));
roles_permissions.put("admin", new HashSet<>(Arrays.asList("user:delete", "user:update")));
super.setName("TestRealm"); //设置Realm名称,可选
} @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//从认证信息获取用户名
String username = (String)principalCollection.getPrimaryPrincipal();
//从数据库或缓存中获取角色、权限数据
Set<String> roles = user_roles.get(username);
Set<String> permissions = new HashSet<>();
for (String role : roles) {
Set<String> set;
if((set = roles_permissions.get(role)) != null) {
permissions.addAll(set);
}
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从主题传过来的认证信息中,获得用户名
String username = (String)authenticationToken.getPrincipal();
//通过用户名从数据库中获取凭证
String password = users.get(username);
if(password != null) {
//不加盐
// return new SimpleAuthenticationInfo(username, password, super.getName());
//加盐
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, super.getName());
simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(salt));
return simpleAuthenticationInfo;
}
return null;
}
}

单元测试:

package com.example.demo_mg;

import com.example.demo_mg.realm.TestRealm;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class DemoMgApplicationTests {
@Test
public void contextLoads() {
} @Test
public void test() {
TestRealm testRealm = new TestRealm(); //设置加密
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5"); //加密算法
matcher.setHashIterations(2); //加密次数,与new Md5Hash("123456",null, 2).toString();的hashIterations参数一致,否则IncorrectCredentialsException
testRealm.setCredentialsMatcher(matcher); //构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(testRealm);
//主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("wzs", "123456");
subject.login(token);
//授权
System.out.println(subject.isAuthenticated());
System.out.println(subject.hasRole("admin"));
System.out.println(subject.isPermitted("user:delete"));
subject.logout();
}
}

MD5加密不可逆,但不加盐存在风险,注册的时候随机生成盐,保存到数据库,这里返回的认证对象带的盐从库里查出来。

Apache Shiro 认证+授权(一)的更多相关文章

  1. 源码分析shiro认证授权流程

    1. shiro介绍 Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户“登录”: 授权 - 访问控制: 密码加密 ...

  2. shiro框架学习-2-springboot整合shiro及Shiro认证授权流程

    1. 添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  3. Apache Shiro 认证过程

    3.1.1    示例 Shiro验证Subjects 的过程中,可以分解成三个不同的步骤: 1. 收集Subjects 提交的Principals(身份)和Credentials(凭证): 2. 提 ...

  4. 【Shiro】三、Apache Shiro认证

    配置好并获取到SecurityManager,代表Shiro正常运行起来了,可以使用Shiro的其它功能. 1.认证流程(API的使用流程) 认证的数据: Principals:标识 ·识别Subje ...

  5. shiro认证授权

    一.shiro基础概念 Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份: Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限:即判断用户 ...

  6. Shiro认证、角色、权限

    Apache Shiro 是 Java 的一个安全框架.Shiro 可以帮助我们完成:认证.授权.加密.会话管理.与 Web 集成.缓存等. Shiro的内置Realm:IniRealm和JdbcRe ...

  7. Apache Shiro 使用手册(三)Shiro 授权

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限. 如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等. 一.授权的三要素 授权有着三 ...

  8. JAVAEE——BOS物流项目10:权限概述、常见的权限控制方式、apache shiro框架简介、基于shiro框架进行认证操作

    1 学习计划 1.演示权限demo 2.权限概述 n 认证 n 授权 3.常见的权限控制方式 n url拦截权限控制 n 方法注解权限控制 4.创建权限数据模型 n 权限表 n 角色表 n 用户表 n ...

  9. Apache shiro集群实现 (四)shiro授权(Authentication)--访问控制

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

随机推荐

  1. P3773 [CTSC2017]吉夫特

    传送门 看到组合数在模 $2$ 意义下的乘积,考虑用 $lucas$ 定理把组合数拆开 $lucas$ 告诉我们,$C(n,m)$ 在模 $k$ 意义下的值,相当于 $n,m$ 在 $k$ 进制下每一 ...

  2. C#学习大纲

    一.C#:    1.进制转换    2.vs界面内容 熟悉软件    3.数据类型 :引用类型 值类型    4.变量 (存储数据)一个变量存储一个数据    5.类型转换    6.运算符:算数运 ...

  3. web笔记全

    1.项目流程与数据库 1.课程体系 阶段1(服务器开发): 项目导入/数据库/JS基础/NodeJS 阶段2(前端核心技术): HTML/AJAX/CSS/bootstrap 阶段3(前端进阶技术): ...

  4. windows使用ubuntu启动linux服务

    有些服务只能在linux中策马奔腾,但是公司配置windows电脑,因此在windows中安装ubuntu服务,再在启动的ubuntu中启动linux服务 系统:win10(其他系统没试过) 安装步骤 ...

  5. 03.LNMP架构-PHP源码包编译部署详细步骤

    一.环境准备 操作系统:CentOS_Server_7.5_x64_1804.iso 部署组件:yasm+libmcrypt+libvpx+tiff+libpng+freetype+jpeg+libg ...

  6. 一、简单的移动端tab头部二级下拉导航栏,向下弹出,向上隐藏

    一.简单的移动端tab头部二级下拉导航栏,向下弹出,向上隐藏 <html lang="en"> <head> <meta charset=" ...

  7. 二 shell 基础

    一   文件的 权限基础 文件有三类权限   user,group,other, 权限分为 r w x  代表数字分别为 4 2 1 修改权限命令 chmod 权限还有特殊权限,在执行的时候代表某一身 ...

  8. 条款7:为多态基类析构函数声明为virtual

    基类指针指向子类对象. 子类对象必须位于堆.因此为了避免泄漏内存资源,当指针不使用时,delete掉每一个对象非常重要.但是如果基类的析构函数不声明为virtual.那么指向子类对象的指针delete ...

  9. runltp出现问题 [

    runltp 623行: if [ "$?" == "0" ]; then 对[解析出了问题. 我灵机一动,是不是sh的问题. which sh /bin/sh ...

  10. QT的总结文章(转)

    ★了解Qt和C++的关系  ★掌握Qt的信号/槽机制的原理和使用方法  ★了解Qt的元对象系统  ★掌握Qt的架构  ★理解Qt的事件模型,掌握其使用的时机 信号与槽.元对象系统.事件模型是Qt机制的 ...