一、shiro基础概念
1.什么是shiro
Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相 当简单,对比 Spring
Security,可能没有 Spring Security 做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的
Shiro 就足够
shiri框架图
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用
户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用
户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信
息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web 支持,可以非常容易的集成到 Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能
把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录
记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过
相应的接口注入给 Shiro 即可。
2.shiro实现原理理解
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager : 相 当 于 SpringMVC 中 的 DispatcherServlet 或 者 Struts2 中的
FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,
这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO中可以使用 Cache 进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本
上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密
3.shiro登录认证
例子1:最简单的登录认证
pom
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <!--shiro认证--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency>
java代码
public class LoginTest {
/**
* 模拟一个最简单的登录
*/
@Test
public void TestAuthentication(){
//1.创建一个SecurityManager类
DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
//创建一个realm域,存储用户账号信息
SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
simpleAccountRealm.addAccount("jzk","1234");
//设置到SecurityManager
defaultSecurityManager.setRealm(simpleAccountRealm);
//2.创建一个主体
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject=SecurityUtils.getSubject();
//3.创建一个登陆token
UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345");
//登陆
try{
subject.login(token);
//查看登录是否成功
System.out.println("login is "+subject.isAuthenticated());
}
catch (UnknownAccountException ex){
System.out.println("登录账号出错");
}
catch (IncorrectCredentialsException e){
System.out.println("密码错误");
}
catch (Exception e){
System.out.println("其他异常");
}
}
}
代码导读
(1)创建一个SecurityManager类,并设置Realm域的账号密码
(2)在SecurityUtils类帮助下撞见一个主体
(3)主体通过一个token进行登录操作
(4)UnknownAccountException 等异常抛出会反应登录的各种情况
例子2:最简单的授权
授权和登录验证类似,需要在realm里增加用户的角色数据
Java代码
/** * 模拟一个最简单的登录授权 */ @Test public void testRoleAuthentication(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个realm域,存储用户账号信息 SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm(); //角色组 String roles[]=new String[]{"admin","approcve","user"}; simpleAccountRealm.addAccount("jzk","1234",roles); //设置到SecurityManager defaultSecurityManager.setRealm(simpleAccountRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("jzk","1234"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
代码导读:
1)在realm里增加角色数据
2)登录后用subject.checkRole或者subject.checkRoles检查是否有一个或者多个权限
4.reaml域详解
reaml是存储用户登录及权限的数据,有很多reaml可以实现
SimpleAccountRealm | |
IniAccountRealm | 保存到ini文件里 |
JDBCAccountRealm | 保存到数据库里 |
CustAccountRealm | 自定义 |
(1)ini域
ini文件
用户
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhang=123,role1,role2
wang=123,role2
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
java代码:
/** * ini领域 */ @Test public void iniAuthentication(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个inirealm域,存储用户账号信息 IniRealm iniRealm=new IniRealm("classpath:ini/user.ini"); //设置到SecurityManager defaultSecurityManager.setRealm(iniRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
ini概述
[main]
#提供了对根对象 securityManager 及其依赖的配置
securityManager=org.apache.shiro.mgt.DefaultSecurityManager
…………
securityManager.realms=$jdbcRealm
[users]
#提供了对用户/密码及其角色的配置,用户名=密码,角色 1,角色 2
username=password,role1,role2
[roles]
#提供了角色及权限之间关系的配置,角色=权限 1,权限 2
role1=permission1,permission2
[urls]
#用于 web,提供了对 web url 拦截相关的配置,url=拦截器[参数],拦截器
/index.html = anon
/admin/** = authc, roles[admin], perms["permission1"]
4)权限验证
[users] jzk=12345,admin,user jth=111,admin [roles] admin=user:delete,user:update user=user:login,user:logout
/** * ini领域权限验证 */ @Test public void iniPpermission(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个inirealm域,存储用户账号信息 IniRealm iniRealm=new IniRealm("classpath:ini/user.ini"); //设置到SecurityManager defaultSecurityManager.setRealm(iniRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //权限验证 System.out.println("=====权限验证===="); System.out.println("======subject.isAuthenticated判断权限,无抛出异常UnauthorizedException=============="); //单个权限 subject.checkPermission("user:create"); System.out.println("Permission[user:create] is "+subject.isAuthenticated()); //多个权限 subject.checkPermissions("user:create","user:update"); System.out.println("Permissions[user:create,user:update] is "+subject.isAuthenticated()); System.out.println("======subject.isPermitted判断权限=============="); //使用isPermission判断权限,返回boolean,boolean[]值不会抛出异常 //单个权限 System.out.println("Permission[user:create] is "+subject.isPermitted("user:create")); //多个权限 System.out.println("Permissions[user:create,user:update] is "+subject.isPermittedAll("user:create","user:update")); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
(5)JDBCAccountRealm验证域
realm数据来自数据库
pom
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
sql
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`password` varchar(255) DEFAULT NULL,
`password_salt` varchar(255) DEFAULT NULL,
`username` varchar(255) NOT NULL,
PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for user_roles
-- ----------------------------
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
`username` varchar(255) DEFAULT NULL,
`role_name` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for roles_permissions
-- ----------------------------
DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
`role_name` varchar(255) NOT NULL,
`permission` varchar(255) NOT NULL,
PRIMARY KEY (`role_name`,`permission`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
java代码
@Test public void jdbcPpermission(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个jdbcRealm域,存储用户账号信息 JdbcRealm jdbcRealm=new JdbcRealm(); DruidDataSource ds=new DruidDataSource(); ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiri?serverTimezone=UTC"); ds.setUsername("root"); ds.setPassword("xbsoft"); jdbcRealm.setDataSource(ds); //权限查询开关,默认为false,不设置将查询不到权限 jdbcRealm.setPermissionsLookupEnabled(true); //设置到SecurityManager defaultSecurityManager.setRealm(jdbcRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //验证权限 subject.checkPermission("user:update"); System.out.println("Permission 【user:update】 is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
导读
为什么我们没有写sql数据,系统能读取,打开JdbcRealm类
protected DataSource dataSource; 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 = ?"; protected boolean permissionsLookupEnabled = false;
他已经内置了查询sql语句
自定义相关查询SQL语句
在我们实际应用中,基本不可能沿用内置的SQL和表,我们则可以自定义密码,角色,权限对应的SQL语句
/** * 自定义sql jdbc领域 */ @Test public void jdbcSQLPpermission(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个jdbcRealm域,存储用户账号信息 JdbcRealm jdbcRealm=new JdbcRealm(); DruidDataSource ds=new DruidDataSource(); ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiri?serverTimezone=UTC"); ds.setUsername("root"); ds.setPassword("xbsoft"); jdbcRealm.setDataSource(ds); //权限查询开关,默认为false,不设置将查询不到权限 jdbcRealm.setPermissionsLookupEnabled(true); //=============设置自定义SQL======== /** 系统自带,需要按照这个规律 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 = ?"; */ //登录sql String authenticationQuery="select pwd from xt_user where login_code=?"; jdbcRealm.setAuthenticationQuery(authenticationQuery); //角色查询SQL String userRolesQuery="SELECT c.role_name FROM xt_user a" + " INNER JOIN xt_user_role b ON a.id=b.user_id " + " INNER JOIN xt_role c ON b.role_id=c.role_id" + " where a.is_ok=1 and a.login_code=?"; jdbcRealm.setUserRolesQuery(userRolesQuery); //查询权限SQL String permissionsQuery="select c.rec_name from xt_role a " + "INNER JOIN xt_role_permission b ON a.role_id=b.role_id " + "INNER JOIN xt_resource c ON b.rec_id=c.rec_id " + "where a.role_name=?"; jdbcRealm.setPermissionsQuery(permissionsQuery); //设置到SecurityManager defaultSecurityManager.setRealm(jdbcRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("13688006635","1"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //验证权限 subject.checkPermission("user:insert"); System.out.println("Permission 【user:insert】 is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
6.自定义Realm类
我们查看JDBCRealm类,其继承了AuthorizingRealm,重点的是实现两个方法,登录验证和权限信息
自定义realm
package com.jyj.shiro; import org.apache.shiro.authc.*; 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.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** 自定义Realm,实现自己的方法 * @class: com.jyj.shiro.CustRealm * @author: jiangzengkui * @company: 教育家 * @create: 2020-12-20 15:13 * @description: * @updateRemark: */ public class CustRealm extends AuthorizingRealm { static Map<String,String> data=new HashMap<>(); static { data.put("jzk","123456"); data.put("dage","1"); } /** * * @param authenticationToken 登录检验 * @return 登录成功信息 * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //得到登录用户账号 String login_code=(String)authenticationToken.getPrincipal(); //得到密码 String pwd=getPwd(login_code); SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(login_code,pwd,getName()); return info; } /** * 获得授权数据 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //得到登录用户账号 String login_code=(String) principalCollection.getPrimaryPrincipal(); //获得角色信息 Set<String> roles=getRole(login_code); //获得权限信息 Set<String> permissions=getPermission(login_code); //new SimpleAuthorizationInfo,设置相关的角色和权限数据 SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.setRoles(roles); info.setStringPermissions(permissions); return info; } /** * realm需要取个名称 * @return */ @Override public String getName() { // TODO Auto-generated method stub return "myRealm"; } /** *这里虚拟了从数据里通过登录账号查询密码信息 * @return */ private String getPwd(String loginCode){ String pwd=null; pwd=data.get(loginCode); if (pwd == null) { throw new UnknownAccountException("No account found for user [" + loginCode + "]"); } return pwd; } /**模拟从数据库 * 通过用户账号获得角色信息 * @param login_code * @return */ private Set<String> getRole(String login_code) { Set<String> roles=new HashSet<>(); roles.add("admin"); roles.add("user"); return roles; } /** * m模拟从数据库获得权限数据 * @param login_code * @return */ private Set<String> getPermission(String login_code) { Set<String> permission=new HashSet<>(); permission.add("user:delete"); permission.add("user:update"); return permission; } }
测试类
/** * 自定义realm域 */ @Test public void customRealm(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个jdbcRealm域,存储用户账号信息 CustRealm custRealm=new CustRealm();//自定义 //设置到SecurityManager defaultSecurityManager.setRealm(custRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("dage","1"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //验证权限 subject.checkPermission("user:update"); System.out.println("Permission 【user:update】 is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
7.加密算法
一般我们在数据库保存的用户密码都是经过加密后的密码,所以我们想把加密后的用户信息传给shiro进行认证,就必须把该密码加密的算法、添加的盐以及散列的次数告诉shiro,因此我们需要配置realm里面的凭证匹配器credentialsMatcher,当用户将账号密码输进来时,shiro就会根据我们设置的加密规则对密码进行加密加盐,然后与realm中查询封装好的数据库数据进行比对认证
(1)设置域的加密算法
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//加密算法
matcher.setHashIterations(1);//加密次数
ream.setCredentialsMatcher(matcher);
MD5算法
import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.crypto.hash.SimpleHash; /** * @class: com.jyj.shiro.TestMd5 * @author: jiangzengkui * @company: 教育家 * @create: 2020-12-20 21:50 * @description: * @updateRemark: */ public class TestMd5 { public static void main(String[] args) { String password = "123456";//要加密的字符串 String salt = "wjw";//盐 Integer hashIterations = 1;//散列次数 //1.不加盐的md5 Md5Hash md5 = new Md5Hash(password); System.out.println(md5.toString()); //2.加盐的md5 md5 = new Md5Hash(password, salt); System.out.println(md5.toString()); //3.加盐再设置散列次数的md5 md5 = new Md5Hash(password, salt, hashIterations); System.out.println(md5.toString()); //4.利用SimpleHash来设置md5(上面三种都可以通过这个来设置,这里举例加盐加散列次数的) //第一个参数是算法名称,这里指定md5,第二个是要加密的密码,第三个参数是加盐,第四个是散列次数 //SimpleHash("md5", password) == Md5Hash md5 = new Md5Hash(password); SimpleHash hash = new SimpleHash("md5", password, salt,hashIterations); System.out.println(hash.toString()); } }
不加盐的MD5
只需要在ream声明里加入加密算法即可,其他的不变
/** * 自定义realm域的加密算法,不加盐 */ @Test public void customCryptNotSaltRealm(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个jdbcRealm域,存储用户账号信息 CustRealm custRealm=new CustRealm();//自定义域 /** * 设置加密算法 */ //如果密码是加密 HashedCredentialsMatcher matcher=new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("md5");//加密算法 matcher.setHashIterations(1);//加密次数 custRealm.setCredentialsMatcher(matcher); //设置到SecurityManager defaultSecurityManager.setRealm(custRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("jzk","123456"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //验证权限 subject.checkPermission("user:update"); System.out.println("Permission 【user:update】 is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
如果用了加了盐,则在域里要把盐加入
package com.jyj.shiro; import org.apache.shiro.authc.*; 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.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** 自定义Realm,实现自己的加密方法 * @class: com.jyj.shiro.CustRealm * @author: jiangzengkui * @company: 教育家 * @create: 2020-12-20 15:13 * @description: * @updateRemark: */ public class CustCryptRealm extends AuthorizingRealm { static Map<String,String> data=new HashMap<>(); static { data.put("jzk","e947d7c6721c1e5c6062984dc30829af");//加了密的密码 data.put("dage","1"); } /** * * @param authenticationToken 登录检验 * @return 登录成功信息 * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //得到登录用户账号 String login_code=(String)authenticationToken.getPrincipal(); //得到密码 String pwd=getPwd(login_code); SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(login_code,pwd,getName()); //SimpleAuthenticationInfo必须设置加盐 info.setCredentialsSalt(ByteSource.Util.bytes("jzk"));//必须加盐 return info; } /** * 获得授权数据 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //得到登录用户账号 String login_code=(String) principalCollection.getPrimaryPrincipal(); //获得角色信息 Set<String> roles=getRole(login_code); //获得权限信息 Set<String> permissions=getPermission(login_code); //new SimpleAuthorizationInfo,设置相关的角色和权限数据 SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.setRoles(roles); info.setStringPermissions(permissions); return info; } /** * realm需要取个名称 * @return */ @Override public String getName() { // TODO Auto-generated method stub return "myRealm"; } /** *这里虚拟了从数据里通过登录账号查询密码信息 * @return */ private String getPwd(String loginCode){ String pwd=null; pwd=data.get(loginCode); if (pwd == null) { throw new UnknownAccountException("No account found for user [" + loginCode + "]"); } return pwd; } /**模拟从数据库 * 通过用户账号获得角色信息 * @param login_code * @return */ private Set<String> getRole(String login_code) { Set<String> roles=new HashSet<>(); roles.add("admin"); roles.add("user"); return roles; } /** * m模拟从数据库获得权限数据 * @param login_code * @return */ private Set<String> getPermission(String login_code) { Set<String> permission=new HashSet<>(); permission.add("user:delete"); permission.add("user:update"); return permission; } public static void main(String[] args){ String password = "123456";//要加密的字符串 String salt = "jzk";//盐 Integer hashIterations = 1;//散列次数 //1.不加盐的md5 Md5Hash md5 = new Md5Hash(password); System.out.println("no salt:=="+md5.toString()); //2.加盐的md5 md5 = new Md5Hash(password, salt); System.out.println("salt:=="+md5.toString()); //3.加盐再设置散列次数的md5 md5 = new Md5Hash(password, salt, hashIterations); System.out.println("salt hashIterations:=="+md5.toString()); } }
测试类加密设置不变
//如果密码是加密
HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//加密算法
matcher.setHashIterations(1);//加密次数
custRealm.setCredentialsMatcher(matcher);
JdbcRealm加密加盐
上文实现了自定义realm的加密加盐,如果直接用JdbcRealm类如何加密加盐呢
数据库表
代码
/** * JdbcRealm 加密加盐的算法 */ @Test public void jdbcSQLCryptSaltPermission(){ //1.创建一个SecurityManager类 DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager(); //创建一个jdbcRealm域,存储用户账号信息 JdbcRealm jdbcRealm=new JdbcRealm(); DruidDataSource ds=new DruidDataSource(); ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiri?serverTimezone=UTC"); ds.setUsername("root"); ds.setPassword("xbsoft"); jdbcRealm.setDataSource(ds); //权限查询开关,默认为false,不设置将查询不到权限 jdbcRealm.setPermissionsLookupEnabled(true); //=============设置自定义SQL======== /** 系统自带,需要按照这个规律 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 = ?"; */ //登录sql String authenticationQuery="select pwd,salt from xt_user where login_code=?"; jdbcRealm.setAuthenticationQuery(authenticationQuery); //角色查询SQL String userRolesQuery="SELECT c.role_name FROM xt_user a" + " INNER JOIN xt_user_role b ON a.id=b.user_id " + " INNER JOIN xt_role c ON b.role_id=c.role_id" + " where a.is_ok=1 and a.login_code=?"; jdbcRealm.setUserRolesQuery(userRolesQuery); //查询权限SQL String permissionsQuery="select c.rec_name from xt_role a " + "INNER JOIN xt_role_permission b ON a.role_id=b.role_id " + "INNER JOIN xt_resource c ON b.rec_id=c.rec_id " + "where a.role_name=?"; jdbcRealm.setPermissionsQuery(permissionsQuery); //如果密码是加密而且加盐 HashedCredentialsMatcher matcher=new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("md5");//加密算法 matcher.setHashIterations(1);//加密次数 jdbcRealm.setCredentialsMatcher(matcher); /** 加盐:JdbcRealm.SaltStyle.COLUMN表示从表的字段读取盐职 String authenticationQuery="select pwd,salt from xt_user where login_code=?"; 第二个值表示从数据库字段读取盐值 */ jdbcRealm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN); //设置到SecurityManager defaultSecurityManager.setRealm(jdbcRealm); //2.创建一个主体 SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject=SecurityUtils.getSubject(); //3.创建一个登陆token UsernamePasswordToken token=new UsernamePasswordToken("13688006635","123456"); //登陆 try{ subject.login(token); //查看登录是否成功 System.out.println("login is "+subject.isAuthenticated()); //==============是否有相关权限 //权限role验证 //单个角色 subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常 System.out.println("admin role is "+subject.isAuthenticated()); //多个角色 subject.checkRoles("admin","user"); System.out.println("roles admin,user is "+subject.isAuthenticated()); //验证权限 subject.checkPermission("user:insert"); System.out.println("Permission 【user:insert】 is "+subject.isAuthenticated()); //退出 subject.logout(); } catch (UnknownAccountException ex){ System.out.println("登录账号出错"); } catch (IncorrectCredentialsException e){ System.out.println("密码错误"); } catch (UnauthorizedException e){ System.out.println("没有相关权限"); } catch (Exception e){ System.out.println("其他异常"); } }
二、权限过滤