咱們知道shiro這個框架提供了信息認證和受權的功能性接口,可是shiro是不會幫咱們維護數據的,shiro中的用戶信息以及用戶所對應的權限都是須要咱們從數據庫查詢出來而後傳給shiro相對應的接口,所以單單一個jdbcRealm已經沒法知足咱們的需求了,由於jdbcRealm是寫死了的,裏面查詢的只能是users表。因此,爲了知足咱們的需求,咱們必須自定義realm,從而才能不侷限於一張表的數據查詢,還能加本身的一些判斷邏輯。下面講講怎麼實現自定義realm。java
自定義realm首先咱們就要寫一個realm,而這個realm咱們通常要繼承AuthorizingRealm類,由於這個類裏面就有實現接收用戶認證信息和接收用戶權限信息的兩個方法,而realm就是用來從數據庫查詢這些數據的。下面是我自定義的realm:mysql
package com.wujianwu.realm; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.sql.DataSource; 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.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import com.wujianwu.bean.User; public class MyRealm extends AuthorizingRealm{ private DataSource dataSource; @Override public String getName() { // TODO Auto-generated method stub return "myRealm"; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String userName = (String) token.getPrincipal(); SimpleAuthenticationInfo info = null; User user = getUserInfo(userName); System.out.println("用戶名"+user.getUsername()+"=============="); info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName()); return info; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { // TODO Auto-generated method stub return null; } public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } private User getUserInfo(String username){ User user = new User(); String sql = "select username,password from users where username=?"; Connection connection = null; PreparedStatement statement = null; ResultSet set = null; try { connection = dataSource.getConnection(); statement = connection.prepareStatement(sql); statement.setString(1, username); set = statement.executeQuery(); if(set.next()) { String username1 = set.getString(1); String password = set.getString(2); user.setPassword(password); user.setUsername(username1); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { closeAll(connection, set, statement); } return user; } private void closeAll(Connection conn,ResultSet set,PreparedStatement statement) { try { if(set != null) { set.close(); } if(statement != null) { statement.close(); } if(conn != null) { conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
能夠看到,個人realm只編寫了接收認證信息這個方法的代碼,畢竟咱們還沒學到受權= =。我這裏其實也是模仿了jdbcRealm,在MyRealm中設置了數據源的屬性,而後經過subject.login(token)中傳過來的token來獲取用戶輸入的身份,而後根據身份區數據庫查詢出用戶的憑證和其餘信息(能夠看到這裏我用的仍是users表,是由於我太懶了,直接用現有的表,固然這裏是能夠查詢任何你想要查詢的表的!),而後用simpleAuthenticationInfo封裝用戶的信息返回給shiro去認證,實際上這裏咱們只是從數據庫將用戶數據查詢出來封裝而後返回給shiro而已,最終的認證仍是shiro幫咱們完成的。(注意,記得關閉資源,如我代碼中的closeAll方法)sql
好了,寫好了自定義的realm,咱們固然還須要把它配置到SecurityManager中去,下面是配置文件shiro.ini的具體實現:數據庫
[main] dataSource=com.mchange.v2.c3p0.ComboPooledDataSource dataSource.driverClass=com.mysql.jdbc.Driver dataSource.jdbcUrl=jdbc:mysql://localhost:3306/test dataSource.user=root dataSource.password=root myRealm=com.wujianwu.realm.MyRealm myRealm.dataSource=$dataSource securityManager.realm=$myRealm
能夠看到,咱們配置了myRealm中的dataSource,並將最終的myRealm設置到SecurityManager中去,從而實現了咱們自定義realm的配置apache
而後就是測試了,仍是那一套熟悉的流程= =,代碼以下(此次懶得寫註釋了):session
package com.wujianwu.test; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestMyRealm { private static final Logger logger = LoggerFactory.getLogger(TestMyRealm.class); public static void main(String[] args) { Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); SecurityManager securityManager = factory.getInstance(); SecurityUtils.setSecurityManager(securityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123456"); try { subject.login(token); if(subject.isAuthenticated()) { logger.info("用戶登陸認證成功"); } } catch (AuthenticationException e) { // TODO Auto-generated catch block e.printStackTrace(); logger.error("用戶名或者密碼錯誤,登陸失敗"); } } }
數據庫中對應的數據以下:app
運行完控制檯打印以下:框架
2019-07-28 16:24:50,001 INFO [com.mchange.v2.log.MLog] - MLog clients using slf4j logging. 2019-07-28 16:24:50,405 INFO [com.mchange.v2.c3p0.C3P0Registry] - Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10] 2019-07-28 16:24:50,520 INFO [org.apache.shiro.config.IniSecurityManagerFactory] - Realms have been explicitly set on the SecurityManager instance - auto-setting of realms will not occur. 2019-07-28 16:24:50,545 INFO [com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource] - Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 2zm2h6a4fg70y51p9sdrv|7e0ea639, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 2zm2h6a4fg70y51p9sdrv|7e0ea639, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/test, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ] 用戶名zhangsan============== 2019-07-28 16:24:50,842 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler... 2019-07-28 16:24:50,847 INFO [com.wujianwu.test.TestMyRealm] - 用戶登陸認證成功
能夠看到,確確實實調用到了咱們自定義的realm(由於打印出了我在myRealm中想要輸出的東西「用戶名zhangsan==============」)。ide
由於咱們這裏只是單單一個shiro框架,因此咱們在查詢數據庫數據時才用了jdbc那一套流程,以後進行ssm整合時就不用這麼麻煩了,直接在realm中注入mapper,而後調mapper的方法查詢就好了,固然這些都是題外話,之後實現了我也會更新到博客中。測試
以上就是自定義realm的具體實現,有什麼補充或修改的請在評論區留言,謝謝!