一、概述:
官方对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','111111'); #新增一个角色 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