第七章:自定义Realm,从数据库中获取数据进行认证【重点】
工程结构
一、建立数据库,数据库名shiro
用户表users、角色表roles、权限表permissions
一个用户有多个角色,一个角色有多种权限
CREATE TABLE `users` ( `id` int(11) NOT NULL, `userName` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, `roleId` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_ID_ROLES_USERS` (`roleId`), CONSTRAINT `FK_ID_ROLES_USERS` FOREIGN KEY (`roleId`) REFERENCES `roles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `roles` ( `id` int(11) NOT NULL, `roleName` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `permissions` ( `id` int(11) NOT NULL, `permissionName` varchar(50) DEFAULT NULL, `roleId` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_ID_ROLES_PERMISSIONS` (`roleId`), CONSTRAINT `FK_ID_ROLES_PERMISSIONS` FOREIGN KEY (`roleId`) REFERENCES `roles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入测试数据:
二、在pom.xml中添加mysql数据库驱动包
<!-- mysql数据库驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency>
三、添加IO包
<!-- IO包 --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
四、在src/main/resources下创建db.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/shiro?useUnicod=true&characterEncoding=utf-8 userName=root password=123456
五、创建包com.java1234.common,包下创建连接数据库的工具类DBHelper.java
package com.java1234.common; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Properties; /** * 连接数据库 */ public class DBHelper { // 驱动参数 private static Connection conn = null; private static PreparedStatement ps = null; // 连接 public static Connection getConnection(String driverClassName, String url, String userName, String password) throws Exception { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, userName, password); } catch (Exception e) { e.printStackTrace(); } return conn; } public static PreparedStatement getPreparedStatement(Connection conn, String sql) throws Exception { try { ps = conn.prepareStatement(sql); } catch (Exception e) { e.printStackTrace(); } return ps; } // 释放资源 public static void closePreparedStatement(PreparedStatement ps) throws Exception { try { if (ps != null) { ps.close(); ps = null; } } catch (Exception e) { e.printStackTrace(); } } public static void closeConnection(Connection conn) throws Exception { try { if (conn != null) { conn.close(); conn = null; } } catch (Exception e) { e.printStackTrace(); } } public static void closeResultSet(ResultSet rs) throws Exception { try { if (rs != null) { rs.close(); rs = null; } } catch (Exception e) { e.printStackTrace(); } } public static String getValueByKey(String filename, String key) { Properties ps = new Properties(); InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(filename)); ps.load(is); String value = ps.getProperty(key); return value; } catch (Exception e) { e.printStackTrace(); return null; } finally { if (is != null) { try { is.close(); is = null; } catch (IOException e) { e.printStackTrace(); } } } } }
六、创建测试类,测试数据库是否连接成功
package com.java1234.test; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Properties; import com.java1234.common.DBHelper; public class DbTest { public static void main(String[] args) { String filename = "src\\main\\resources\\db.properties"; String driverClassName = getValueByKey(filename, "driverClassName"); String url = getValueByKey(filename, "url"); String userName = getValueByKey(filename, "userName"); String password = getValueByKey(filename, "password"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBHelper.getConnection(driverClassName, url, userName, password); String sql = "SELECT * FROM users"; ps = DBHelper.getPreparedStatement(conn, sql); rs = ps.executeQuery(); while (rs.next()) { Integer db_id = rs.getInt(1); String db_userName = rs.getString(2); String db_password = rs.getString(3); System.out.println(db_id + " | " + db_userName + " | " + db_password); System.out.println(); } } catch (Exception e) { e.printStackTrace(); } finally { try { DBHelper.closeResultSet(rs); DBHelper.closePreparedStatement(ps); DBHelper.closeConnection(conn); } catch (Exception e) { e.printStackTrace(); } } } public static String getValueByKey(String filename, String key) { Properties ps = new Properties(); InputStream is = null; try { is = new BufferedInputStream(new FileInputStream(filename)); ps.load(is); String value = ps.getProperty(key); return value; } catch (Exception e) { e.printStackTrace(); return null; } finally { if (is != null) { try { is.close(); is = null; } catch (IOException e) { e.printStackTrace(); } } } } }
结果: 数据库连接成功!
1 | java1234 | 123456
2 | jack | 123
3 | marry | 234
4 | json | 345
-------------------------------------
七、创建POJO类和DAO类
Users.java
private Integer id; private String userName; private String password; private Roles role;
Roles.java
private Integer id; private String roleName;
Permissions.java
private Integer id; private String permissionName; private Roles role;
UserDao.java
package com.java1234.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.HashSet; import java.util.Set; import com.java1234.common.DBHelper; import com.java1234.entities.Users; public class UserDao { // 数据库参数 private static Connection conn = null; private static PreparedStatement ps = null; private static ResultSet rs = null; // 连接参数 private static String filename = null; private static String jdbc_driverClassName = null; private static String jdbc_url = null; private static String jdbc_userName = null; private static String jdbc_password = null; static { // filename = "src\\main\\resources\\db.properties"; filename = "G:\\Test2\\ShiroWeb\\src\\main\\resources\\db.properties"; jdbc_driverClassName = DBHelper.getValueByKey(filename, "driverClassName"); jdbc_url = DBHelper.getValueByKey(filename, "url"); jdbc_userName = DBHelper.getValueByKey(filename, "userName"); jdbc_password = DBHelper.getValueByKey(filename, "password"); try { conn = DBHelper.getConnection(jdbc_driverClassName, jdbc_url, jdbc_userName, jdbc_password); } catch (Exception e) { e.printStackTrace(); } } /** * 根据用户名获取用户信息 * @param userName 用户输入的用户名 * @return * @throws Exception */ public Users getByUserName(String userName) throws Exception { Users user = null; String sql = "SELECT * FROM users WHERE userName = ? LIMIT 1"; try { ps = DBHelper.getPreparedStatement(conn, sql); ps.setString(1, userName); ResultSet rs = ps.executeQuery(); if (rs.next()) { user = new Users(); user.setId(rs.getInt(1)); user.setUserName(rs.getString(2)); user.setPassword(rs.getString(3)); } } catch (Exception e) { e.printStackTrace(); } return user; } /** * 根据用户名获取用户的角色 * @param userName * @return 角色列表 * @throws Exception */ public Set<String> getRoles(String userName) throws Exception { Set<String> roles = new HashSet<String>(); String sql = "SELECT r.roleName FROM users u LEFT JOIN roles r ON u.roleId = r.id WHERE u.userName = ?"; try { ps = DBHelper.getPreparedStatement(conn, sql); ps.setString(1, userName); rs = ps.executeQuery(); while (rs.next()) { roles.add(rs.getString("roleName")); } } catch (Exception e) { e.printStackTrace(); } return roles; } public Set<String> getPermissions(String userName) throws Exception { Set<String> permissions = new HashSet<String>(); String sql = "SELECT p.permissionName FROM users u LEFT " + "JOIN roles r ON u.roleId = r.id LEFT " + "JOIN permissions p ON r.id = p.roleId WHERE u.userName = ?"; try { ps = DBHelper.getPreparedStatement(conn, sql); ps.setString(1, userName); rs = ps.executeQuery(); while (rs.next()) { permissions.add(rs.getString("permissionName")); } } catch (Exception e) { e.printStackTrace(); } return permissions; } }
八、创建自定义Realm
创建com.java1234.realm.MyRealm.java
package com.java1234.realm; import java.util.Iterator; import java.util.Set; 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 com.java1234.dao.UserDao; import com.java1234.entities.Users; public class MyRealm extends AuthorizingRealm { private UserDao userDao = new UserDao(); /** * 验证当前登录的用户 */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { String userName = (String) token.getPrincipal(); try { Users user = userDao.getByUserName(userName); // 根据用户名获取用户 if (user != null) { AuthenticationInfo info = new SimpleAuthenticationInfo( user.getUserName(), user.getPassword(), "XXX"); return info; } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 为当前用户授予角色和权限, 在用户成功登录后才会执行这个方法 */ @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { /** * 获取用户名,这里的用户名是数据库中的用户名,即从上面的方法doGetAuthenticationInfo() * 中获取的user中的user.getUserName()中获取到的合法的用户名, 通过这个用户名可以获取到用户拥有的角色和权限 */ String userName = (String) principals.getPrimaryPrincipal(); // 绑定用户所拥有的角色和权限 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); try { // 根据用户名获取用户角色,并将其绑定到info中 Set<String> roles = userDao.getRoles(userName); Iterator<String> iterator = roles.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } info.setRoles(roles); // 根据用户名获取用户权限,并剑气绑定到info中 Set<String> permissions = userDao.getPermissions(userName); Iterator<String> iterator2 = permissions.iterator(); while (iterator2.hasNext()) { System.out.println(iterator2.next()); } info.setStringPermissions(permissions); } catch (Exception e) { e.printStackTrace(); } return info; } }
九、将自定义Realm配置到shiro.ini中(包.类的形式)
[main]
authc.loginUrl=/login
roles.unauthorizedUrl=/unauthorized.jsp
perms.unauthorizedUrl=/unauthorized.jsp
myRealm=com.java1234.realm.MyRealm
securityManager.realms=$myRealm
[urls]
/login=anon
/admin?=authc
/student=roles[teacher]
/teacher=perms["user:create"]
十、测试
(1)用户java123456登录
后台打印:
admin
user:*
admin
user:*
(2) 用户jack登录
后台打印:
teacher
student:*
teacher
student:*