本文介绍shiro通过jdbc连接数据库,连接池采用阿里巴巴的druid的连接池
参考文档:https://www.w3cschool.cn/shiro/xgj31if4.html
https://www.w3cschool.cn/shiro/h5it1if8.html
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"> <artifactId>shiro-example-chapter2</artifactId> <groupId>com.github.zhangkaitao</groupId> <version>SNAPSHOT</version> <modelVersion>4.0.0</modelVersion> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.16</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.8</version> </dependency> </dependencies> </project>
shiro.ini 文件的配置
[main]
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=123456
jdbcRealm.dataSource=$dataSource
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
securityManager.realms=$jdbcRealm
jdbcRealm shiro框架已经集成的安全域,安全域的作用是将资源(数据库,缓存,磁盘文件等)中用户身份的集合和需要匹配的集合进行验证,决定登录用户的身份是否能验证成功的功能。如果要自定义安全域,只要继承
AuthorizingRealm 这个接口。实现doGetAuthenticationInfo() 身份验证这个方法即可。
测试类的方法
public class TestJdbcLogin{
@Test public void testJDBCRealm() { //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini"); //2、得到SecurityManager实例 并绑定给SecurityUtils org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证) Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123"); try { //4、登录,即身份验证 subject.login(token); } catch (IncorrectCredentialsException e) { //5、身份验证失败 e.printStackTrace(); System.out.println("密码错误"); }catch (DisabledAccountException e){ System.out.println("禁用的账户"); } Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录 //6、退出 subject.logout(); }
}
JdbcRealm 类
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.apache.shiro.realm.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import javax.sql.DataSource; import org.apache.shiro.authc.AccountException; 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.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.config.ConfigurationException; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.JdbcUtils; import org.apache.shiro.util.ByteSource.Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JdbcRealm extends AuthorizingRealm { protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?"; protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?"; protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?"; protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?"; private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class); 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; protected JdbcRealm.SaltStyle saltStyle; public JdbcRealm() { this.saltStyle = JdbcRealm.SaltStyle.NO_SALT; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setAuthenticationQuery(String authenticationQuery) { this.authenticationQuery = authenticationQuery; } public void setUserRolesQuery(String userRolesQuery) { this.userRolesQuery = userRolesQuery; } public void setPermissionsQuery(String permissionsQuery) { this.permissionsQuery = permissionsQuery; } public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) { this.permissionsLookupEnabled = permissionsLookupEnabled; } public void setSaltStyle(JdbcRealm.SaltStyle saltStyle) { this.saltStyle = saltStyle; if (saltStyle == JdbcRealm.SaltStyle.COLUMN && this.authenticationQuery.equals("select password from users where username = ?")) { this.authenticationQuery = "select password, password_salt from users where username = ?"; } } protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken)token; String username = upToken.getUsername(); if (username == null) { throw new AccountException("Null usernames are not allowed by this realm."); } else { Connection conn = null; SimpleAuthenticationInfo info = null; try { String salt; try { conn = this.dataSource.getConnection(); String password = null; salt = null; switch(this.saltStyle) { case NO_SALT: password = this.getPasswordForUser(conn, username)[0]; break; case CRYPT: throw new ConfigurationException("Not implemented yet"); case COLUMN: String[] queryResults = this.getPasswordForUser(conn, username); password = queryResults[0]; salt = queryResults[1]; break; case EXTERNAL: password = this.getPasswordForUser(conn, username)[0]; salt = this.getSaltForUser(username); } if (password == null) { throw new UnknownAccountException("No account found for user [" + username + "]"); } info = new SimpleAuthenticationInfo(username, password.toCharArray(), this.getName()); if (salt != null) { info.setCredentialsSalt(Util.bytes(salt)); } } catch (SQLException var12) { salt = "There was a SQL error while authenticating user [" + username + "]"; if (log.isErrorEnabled()) { log.error(salt, var12); } throw new AuthenticationException(salt, var12); } } finally { JdbcUtils.closeConnection(conn); } return info; } } private String[] getPasswordForUser(Connection conn, String username) throws SQLException { boolean returningSeparatedSalt = false; String[] result; switch(this.saltStyle) { case NO_SALT: case CRYPT: case EXTERNAL: result = new String[1]; break; case COLUMN: default: result = new String[2]; returningSeparatedSalt = true; } PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(this.authenticationQuery); ps.setString(1, username); rs = ps.executeQuery(); for(boolean foundResult = false; rs.next(); foundResult = true) { if (foundResult) { throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique."); } result[0] = rs.getString(1); if (returningSeparatedSalt) { result[1] = rs.getString(2); } } } finally { JdbcUtils.closeResultSet(rs); JdbcUtils.closeStatement(ps); } return result; } protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { throw new AuthorizationException("PrincipalCollection method argument cannot be null."); } else { String username = (String)this.getAvailablePrincipal(principals); Connection conn = null; Set<String> roleNames = null; Set permissions = null; try { conn = this.dataSource.getConnection(); roleNames = this.getRoleNamesForUser(conn, username); if (this.permissionsLookupEnabled) { permissions = this.getPermissions(conn, username, roleNames); } } catch (SQLException var11) { String message = "There was a SQL error while authorizing user [" + username + "]"; if (log.isErrorEnabled()) { log.error(message, var11); } throw new AuthorizationException(message, var11); } finally { JdbcUtils.closeConnection(conn); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames); info.setStringPermissions(permissions); return info; } } protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; LinkedHashSet roleNames = new LinkedHashSet(); try { ps = conn.prepareStatement(this.userRolesQuery); ps.setString(1, username); rs = ps.executeQuery(); while(rs.next()) { String roleName = rs.getString(1); if (roleName != null) { roleNames.add(roleName); } else if (log.isWarnEnabled()) { log.warn("Null role name found while retrieving role names for user [" + username + "]"); } } } finally { JdbcUtils.closeResultSet(rs); JdbcUtils.closeStatement(ps); } return roleNames; } protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException { PreparedStatement ps = null; LinkedHashSet permissions = new LinkedHashSet(); try { ps = conn.prepareStatement(this.permissionsQuery); Iterator i$ = roleNames.iterator(); while(i$.hasNext()) { String roleName = (String)i$.next(); ps.setString(1, roleName); ResultSet rs = null; try { rs = ps.executeQuery(); while(rs.next()) { String permissionString = rs.getString(1); permissions.add(permissionString); } } finally { JdbcUtils.closeResultSet(rs); } } } finally { JdbcUtils.closeStatement(ps); } return permissions; } protected String getSaltForUser(String username) { return username; } public static enum SaltStyle { NO_SALT, CRYPT, COLUMN, EXTERNAL; private SaltStyle() { } } }
标红的查询语句正是shiro框架已经集成的查询语句,所以建表的时候要根据相应的表名建表
另外可以参考这篇文章:http://blog.51cto.com/luchunli/1828080