一、概述:

  官方对Realm(领域)的描述:https://www.infoq.com/articles/apache-shiro

  

  其功能本质上是一个安全特定的DAO,用于链接数据持久层(任何形式的都可以:数据库、properties文件,xml文件等),获取数据给Shiro使用。

二、数据库的搭建:

  创建数据库及表:

DROP DATABASE IF EXISTS `apptest`;
CREATE DATABASE `apptest` DEFAULT CHARACTER SET utf8 ; USE `apptest` ; DROP TABLE IF EXISTS `tb_customer`;
CREATE TABLE `tb_customer` (
`col_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`col_loginName` varchar(50) DEFAULT NULL COMMENT '登录名',
`col_password` varchar(128) DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`col_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表'; DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role` (
`col_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`col_roleName` varchar(45) DEFAULT NULL COMMENT '角色名称',
PRIMARY KEY (`col_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='角色表'; DROP TABLE IF EXISTS `tb_limit`;
CREATE TABLE `tb_limit` (
`col_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`col_limitName` varchar(45) DEFAULT NULL COMMENT '权限名称',
PRIMARY KEY (`col_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='权限表'; DROP TABLE IF EXISTS `tb_ref_customer_role`;
CREATE TABLE `tb_ref_customer_role` (
`col_customerId` int(11) NOT NULL COMMENT '用户主键id',
`col_roleId` int(11) NOT NULL COMMENT '角色主键id',
PRIMARY KEY (`col_customerId`,`col_roleId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色管理表'; DROP TABLE IF EXISTS `tb_ref_role_limit`;
CREATE TABLE `tb_ref_role_limit` (
`col_roleId` int(11) NOT NULL COMMENT '角色主键id',
`col_limitId` int(11) NOT NULL COMMENT '权限主键id',
PRIMARY KEY (`col_roleId`,`col_limitId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限管理表';

  插入测试数据:

USE `apptest` ;
#新增一个用户
INSERT INTO `apptest`.`tb_customer`(`col_loginName`,`col_password`)VALUES('sunnywen','');
#新增一个角色
INSERT INTO `apptest`.`tb_role`(`col_roleName`)VALUES('admin');
#新增4个权限
INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:create');
INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:update');
INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:query');
INSERT INTO `apptest`.`tb_limit`(`col_limitName`)VALUES('admin:delete');
#插入用户角色关联表
INSERT INTO `apptest`.`tb_ref_customer_role`(`col_customerId`,`col_roleId`)VALUES(1,1);
#插入角色权限管理表
INSERT INTO `apptest`.`tb_ref_role_limit`(`col_roleId`,`col_limitId`)VALUES(1,1);
INSERT INTO `apptest`.`tb_ref_role_limit`(`col_roleId`,`col_limitId`)VALUES(1,2);
INSERT INTO `apptest`.`tb_ref_role_limit`(`col_roleId`,`col_limitId`)VALUES(1,3);

  最后结果:

三、Java代码的实现:

  3.1、新建一个maven工程

  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>org.yoki.edu</groupId>
<artifactId>ShiroLearn</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<version.base>1.0-RELEASE</version.base>
<version.auth.shiro>1.2.3</version.auth.shiro>
<version.logger.log4j>1.2.9</version.logger.log4j>
</properties> <dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
<!-- configure shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${version.auth.shiro}</version>
</dependency>
<!-- configure logging -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies> </project>

  非maven工程的参考jar包如下:

  目录结构:

  3.2、创建实体类:

  用户实体类LoginAccount

package org.yoki.edu;

/**
* Created by SunnyWen on 2017/7/3.
*/
public class LoginAccount { private Integer id ;
//用户登录名
private String loginName ;
//用户登录密码
private String password ; /*
* 省略setter、getter方法
*/
}

  角色实体类Role

  

package org.yoki.edu;

import java.util.ArrayList;
import java.util.List; /**
* Created by SunnyWen on 2017/7/3.
*/
public class Role { private Integer id ;
//角色名称
private String roleName ;
//权限名称List
private List<String> limitList = new ArrayList<>();
/*
* 省略setter、getter方法
*/ }

  3.3、创建数据库连接工具BusinessManager.java:

package org.yoki.edu;

import java.sql.*;
import java.util.ArrayList;
import java.util.List; /**
* Created by SunnyWen on 2017/7/3.
*/
public class BusinessManager { private static final String driver = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost:3306/apptest?Unicode=true&characterEncoding=UTF-8";
private static final String user = "root";
private static final String password = "root"; private static Connection connection ; static{
try {
Class.forName(driver);
connection = DriverManager.getConnection(url , user, password);
System.out.println("Connect database success !!!");
}catch (Exception e){
System.out.println("Connect database failure !!!");
e.printStackTrace();
} } public static Connection getConnection(){
if(null == connection){
synchronized (Connection.class){
if(null == connection) {
connection = buildConnection();
}
}
}
return connection ;
} private static Connection buildConnection(){
Connection connection = null ;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url , user, password);
}catch (Exception e){
e.printStackTrace();
}
return connection ;
} /**
* 根据用户名获取角色及角色下的权限
* @param name
* @return
*/
public List<Role> listRoleByUserName(String name){
StringBuffer sql1 = new StringBuffer() ;
List<Role> list = new ArrayList<>() ;
sql1.append("select col_id , col_roleName from tb_role where col_id in( ") ;
sql1.append(" select distinct(col_roleId) from tb_ref_customer_role where col_customerId in ( " ) ;
sql1.append(" select col_id from tb_customer where col_loginName = ? " ) ;
sql1.append(" )" ) ;
sql1.append(") ;") ;
Connection connection = BusinessManager.getConnection() ;
try {
PreparedStatement statement = connection.prepareStatement(sql1.toString());
statement.setString(1 , name);
ResultSet set = statement.executeQuery() ;
while(set.next()){
Role role = new Role() ;
role.setId(set.getInt(1)) ;
role.setRoleName(set.getString(2)); ;
//根据角色ID获取权限名称
StringBuffer sql2 = new StringBuffer() ;
sql2.append("select col_limitName from tb_limit where col_id in (" ) ;
sql2.append(" select distinct(col_limitId) from tb_ref_role_limit where col_roleId = ?" ) ;
sql2.append(")" ) ;
statement = connection.prepareStatement(sql2.toString());
statement.setInt(1 , role.getId());
ResultSet set2 = statement.executeQuery() ;
while (set2.next()){
role.getLimitList().add(set2.getString(1));
}
list.add(role);
}
}catch (Exception e){
e.printStackTrace();
}
return list ;
} /**
* 根据用户名获取用户
* @param name
* @return
*/
public LoginAccount getLoginAccount(String name){
StringBuffer sb = new StringBuffer() ;
LoginAccount loginAccount = null ;
sb.append("select col_loginName , col_password from tb_customer where col_loginName = ?") ;
Connection connection = BusinessManager.getConnection() ;
try {
PreparedStatement statement = connection.prepareStatement(sb.toString());
statement.setString(1 , name);
ResultSet set = statement.executeQuery() ;
if(set.next()){
loginAccount = new LoginAccount() ;
loginAccount.setLoginName(set.getString(1));
loginAccount.setPassword(set.getString(2));
}
}catch (Exception e){
e.printStackTrace();
}
return loginAccount ;
} }

  3.4、继承Realm,实现用户的认证授权:

  CustomSecurity.java

package org.yoki.edu;

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.Collection;
import java.util.List; /**
* Created by SunnyWen on 2017/7/3.
* 继承AuthorizingRealm
*/
public class CustomSecurityRealm extends AuthorizingRealm { //数据库链接工具
private BusinessManager businessManager = new BusinessManager(); /**
* 获取用户的授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) {
String username = (String) principals.fromRealm(getName()).iterator().next();
if (username != null) {
// 查询用户授权信息
Collection<Role> pers = businessManager.listRoleByUserName(username);
if (pers != null && !pers.isEmpty()) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for (Role role : pers){
//加入角色
info.addRole(role.getRoleName());
List<String> limitList = role.getLimitList() ;
for(String s : limitList){
//加入权限
info.addStringPermission(s);
}
}
return info;
}
}
return null;
} /**
* 获取用户的认证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
//用户名密码Token
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
// 通过表单接收的用户名
String username = token.getUsername();
if (username != null && !"".equals(username)) {
//获取数据库中的用户
LoginAccount account = businessManager.getLoginAccount(username);
if (account != null) {
return new SimpleAuthenticationInfo(
account.getLoginName(), account.getPassword(), getName());
}
}
return null;
}   
  /**
  * 用户认证方式,方法为父类org.apache.shiro.realm.AuthenticatingRealm的方法<BR>
  * 复写此方法可以更改认证方式
  * @param token
  * @param info
  * @throws AuthenticationException
  */
  @Override
  protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    //你可以实现自己的AuthenticationToken,如下
    //if(token instanceof MyAuthenticationToken){
    //  MyAuthenticationToken myToken = (MyAuthenticationToken)token ;
    //  if(myToken.isWeChatLoginFlag()){
    //    String openId = myToken.getOpenId() ;
    //    Customer customer = Customer.selectOneByOpenId(openId);
    //    if(null == customer){
    //      String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
    //      throw new IncorrectCredentialsException(msg);
    //    }
    //    return;
    //  }
    //}     //下方为源码,使用MD5校验方式校验用户名密码
  CredentialsMatcher cm = getCredentialsMatcher();
  if (cm != null) {
  if (!cm.doCredentialsMatch(token, info)) {
  //not successful - throw an exception to indicate this:
  String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
  throw new IncorrectCredentialsException(msg);
  }
  } else {
  throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
"credentials during authentication. If you do not wish for credentials to be examined, you " +
"can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
  }
  }
}

  shiro.ini配置文件

[main]
customSecurityRealm=org.yoki.edu.CustomSecurityRealm
#配置SecurityManager的realm,可以配置多个,使用逗号隔开
securityManager.realms=$customSecurityRealm

  3.5、测试:

package org.yoki.edu;

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; /**
* Created by SunnyWen on 2017/7/3.
*/
public class MainTest { public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("sunnywen", "222222");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
System.out.println("用户: " + token.getPrincipal() + " 不存在!!!");
} catch (IncorrectCredentialsException ice) {
System.out.println("用户: " + token.getPrincipal() + " 密码错误!!!");
try {
System.out.println("用户: " + token.getPrincipal() + " 再次尝试登陆!!!");
token = new UsernamePasswordToken("sunnywen", "111111");
token.setRememberMe(true);
currentUser.login(token);
} catch (UnknownAccountException uae) {
System.out.println("用户: " + token.getPrincipal() + " 不存在!!!");
} catch (IncorrectCredentialsException ice2) {
System.out.println("用户: " + token.getPrincipal() + " 密码错误!!!");
} catch (LockedAccountException lae) {
System.out.println("用户: " + token.getPrincipal() + " 已经被冻结!!!");
} catch (AuthenticationException ae) {
}
} catch (LockedAccountException lae) {
System.out.println("用户: " + token.getPrincipal() + " 已经被冻结!!!");
} catch (AuthenticationException ae) {
}
}
if(null != currentUser.getPrincipal())
System.out.println("用户: " + currentUser.getPrincipal() + " 登录成功!!!"); if (currentUser.hasRole("admin")) {
System.out.println("用户: " + currentUser.getPrincipal() + " 拥有角色'admin'");
} else {
System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有角色'admin'");
} if (currentUser.isPermitted("admin:create")) {
System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:create'");
} else {
System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:create'");
} if (currentUser.isPermitted("admin:update")) {
System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:update'");
} else {
System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:update'");
} if (currentUser.isPermitted("admin:query")) {
System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:query'");
} else {
System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:query'");
} if (currentUser.isPermitted("admin:delete")) {
System.out.println("用户: " + currentUser.getPrincipal() + " 拥有权限'admin:delete'");
} else {
System.out.println("对不起,用户: " + currentUser.getPrincipal() + " 尚未拥有权限'admin:delete'");
} currentUser.logout();
try {
Thread.sleep(500);
}catch (Exception e){
e.printStackTrace();
}
} }

  结果:

Connect database success !!!
用户: sunnywen 密码错误!!!
用户: sunnywen 再次尝试登陆!!!
用户: sunnywen 登录成功!!!
用户: sunnywen 拥有角色'admin'
用户: sunnywen 拥有权限'admin:create'
用户: sunnywen 拥有权限'admin:update'
用户: sunnywen 拥有权限'admin:query'
对不起,用户: sunnywen 尚未拥有权限'admin:delete'

转载请标明转载出处 : https://i.cnblogs.com/EditPosts.aspx?postid=7115108

Apcahe Shiro学习笔记(二):通过JDBC进行权限控制的更多相关文章

  1. Apcahe Shiro学习笔记(一):简介及运行官方Demo

    一.Apache Shrio: apache shiro 是一个功能强大和易于使用的Java安全框架,为开发人员提供一个直观而全面的的解决方案的认证,授权,加密,会话管理. 支持认证跨一个或多个数据源 ...

  2. Shiro学习笔记(二)

    首先还是先搭建工程运行环境  依旧搭建的是Maven工程,如果不是Maven 也可以去网上找jar包然后导入 (我使用Maven主要是找依赖配置文件就行,我自己导jar包的时候就是很容易报错) 还是先 ...

  3. linux —— 学习笔记(用户管理与权限控制)

    目录:1.用户的创建和管理    2.组的创建和管理 3.文件执行权限的控制 4.不用密码执行sudo 1.用户的创建和管理 用户的创建和管理: useradd.usermod . userdel . ...

  4. JDBC学习笔记二

    JDBC学习笔记二 4.execute()方法执行SQL语句 execute几乎可以执行任何SQL语句,当execute执行过SQL语句之后会返回一个布尔类型的值,代表是否返回了ResultSet对象 ...

  5. Shiro学习笔记总结,附加" 身份认证 "源码案例(一)

    Shiro学习笔记总结 内容介绍: 一.Shiro介绍 二.subject认证主体 三.身份认证流程 四.Realm & JDBC reaml介绍 五.Shiro.ini配置介绍 六.源码案例 ...

  6. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  7. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  8. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  9. JMX学习笔记(二)-Notification

    Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...

随机推荐

  1. HDU——4565So Easy!(矩阵快速幂)

    So Easy! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total S ...

  2. dom方法insertAfter的实现

    在dom的原生api中,只用insertBefore,没有insertAfter,借助原有的api,可以模拟一个insterAfter. function insterAfter(newElement ...

  3. Java面试题之有没有有顺序的Map实现类,如果有,他们是怎么实现有序的?

    Hashmap和Hashtable 都不是有序的. TreeMap和LinkedHashmap都是有序的.(TreeMap默认是key升序,LinkedHashmap默认是数据插入顺序) TreeMa ...

  4. javaweb学习总结(十一)——使用Cookie进行会话管理(转)

    一.会话的概念 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾 ...

  5. 洛谷 P 1387 最大正方形

    题目描述 在一个n*m的只包含0和1的矩阵里找出一个不包含0的最大正方形,输出边长. 输入输出格式 输入格式: 输入文件第一行为两个整数n,m(1<=n,m<=100),接下来n行,每行m ...

  6. 标准C程序设计七---72

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  7. Python入门--20--类、对象

    OO=Object Oriented 面向对象 python是面向对象的编程语言 OO的特征: 1.封装:把一堆东西都扔到一起,变为一个类 2.继承:假如一个类里面 3.多态:不同的类有相同名称的函数 ...

  8. java parse 带英文单词的日期字符串(转化新浪微博api返回的时间)

    String str = "Sun Sep 23 00:32:57 +0800 2012"; SimpleDateFormat dateFormat = new SimpleDat ...

  9. CCCC L2-024 部落【并查集】

    https://www.patest.cn/contests/gplt/L2-024 首先在一行中输出这个社区的总人数.以及互不相交的部落的个数.随后对每一次查询,如果他们属于同一个部落,则在一行中输 ...

  10. ZSTU 4241 圣杯战争(ST表+二分)

    题目链接  ZSTU 4241 问题转化为有很多区间,现在每次给定一个区间求这个区间和之前所有区间中的某一个的交集的最大长度. 强制在线. 首先我们把所有的区间预处理出来. 然后去重(那些被包含的小区 ...