posts - 156,  comments - 601,  trackbacks - 0
          注:例子的原作者是 Paul Feuer和John Musser,我在上面加入些注釋以便更好的理解Jaas的實現
          首先,我們來看一下 JAAS 一個認證操作的實現流程

          先看一下這個認證操作會使用的接口如下:

          javax.security.auth.callback.CallbackHandler
          javax.security.auth.spi.LoginModule

          下面看一下這個基于JAAS認證的實現流程:
          JaasTest 實現一個登錄的請求操作:
           1 public class JaasTest {
           2 
           3     public static void main(String[] args) {
           4 
           5         boolean loginSuccess = false;
           6         Subject subject = null;
           7 
           8         try {
           9                 // ConsoleCallbackHandler 實現CallbackHandler接口 處理用戶名和密碼(這里從鍵盤輸入)
          10             ConsoleCallbackHandler cbh = new ConsoleCallbackHandler();
          11                         // 啟動LoginContext, 裝載LoginModule 
          12                         //login module配置文件,通過java -Djava.security.auth.login.config=jaas.config指定
          13                         //其中 example就是module名字
          14             LoginContext lc = new LoginContext("Example", cbh);
          15 
          16             try {
          17                 lc.login(); //進行登錄,它會回調 CallbackHandler的handle方法,要求
          18                 //handle方法把傳入的參數Callback[]中找到NameCallback類型,調用NameCallback.setName把
          19                 //用戶名傳進去,找到PasswordCallback類型,調用PasswordCallback.setPassword方法把密碼傳進去
          20                 //如果登錄失敗,則把拋LoginException異常
          21                 loginSuccess = true;
          22                                 //登錄成功后,可以返回Subject對象 1.(保存一組(Set)Principal對象),一般保存人員信息
          23                                 //2.(保存一組(Set) Credential Object getPublicCredentials(Class<T> c) 一般保存密碼
          24                 subject = lc.getSubject();
          25 
          26                 Iterator it = subject.getPrincipals().iterator();
          27                 while (it.hasNext()) 
          28                     System.out.println("Authenticated: " + it.next().toString());
          29                             
          30                 it = subject.getPublicCredentials(Properties.class).iterator();
          31                 while (it.hasNext()) 
          32                     ((Properties)it.next()).list(System.out);
          33 
          34                 lc.logout(); //注銷
          35             } catch (LoginException lex) {
          36                 System.out.println(lex.getClass().getName() + "" + lex.getMessage());
          37             }
          38         } catch (Exception ex) {
          39             System.out.println(ex.getClass().getName() + "" + ex.getMessage());
          40         }
          41 
          42         System.exit(0);
          43     }
          44 }


          實現步驟如下:
          A:
          //創建LoginContext實例,把login module 名稱和CallbackHandler接口實現
          LoginContext lc = new LoginContext("Example", cbh);

          B:
          lc.login //進行登錄操作,此時,會根據通過java -Djava.security.auth.login.config=jaas.config指定
          //的配置, 加jaas.config文件 jaas.config寫法如下:
          Example {
             RdbmsLoginModule required debug="true" url="jdbc:mysql://localhost/jaasdb?user=root&password=pass" driver="org.gjt.mm.mysql.Driver";
          };

          //login module name
          Example1 {
               //RdbmsLoginModule: module class name
               //required 固定
               //debug url driver都為參數,可以在LoginModule實現類的 initialize(Subject subject, CallbackHandler callbackHandler,
             //         Map sharedState, Map options)方法中取得
             RdbmsLoginModule required debug="true" url="jdbc:mysql://localhost/jaasdb?user=root&password=pass" driver="org.gjt.mm.mysql.Driver";
          };

          接下來,LoginContext會去 回調 CallbackHandler的handle(Callback[] callbacks)方法
          handle方法把傳入的參數Callback[]中找到NameCallback類型,調用NameCallback.setName把
          用戶名傳進去,找到PasswordCallback類型,調用PasswordCallback.setPassword方法把密碼傳進去
          下面來看一下ConsoleCallbackHandler 實現代碼:

           1 public class ConsoleCallbackHandler implements CallbackHandler {
           2 
           3     /**
           4      * <p>Creates a callback handler that prompts and reads from the
           5      * command line for answers to authentication questions.
           6      * This can be used by JAAS applications to instantiate a
           7      * CallbackHandler.
           8      */
           9     public ConsoleCallbackHandler() {
          10     }
          11 
          12     /**
          13      * Handles the specified set of callbacks.
          14      * This class supports NameCallback and PasswordCallback.
          15      *
          16      * @param   callbacks the callbacks to handle
          17      * @throws  IOException if an input or output error occurs.
          18      * @throws  UnsupportedCallbackException if the callback is not an
          19      * instance of NameCallback or PasswordCallback
          20      */
          21     public void handle(Callback[] callbacks) 
          22         throws java.io.IOException, UnsupportedCallbackException {
          23 
          24         for (int i = 0; i < callbacks.length; i++) {
          25                         System.out.println(callbacks[i]);
          26             if (callbacks[i] instanceof NameCallback) {
          27                 System.out.print(((NameCallback)callbacks[i]).getPrompt());
          28                 String user=(new BufferedReader(new InputStreamReader(System.in))).readLine();
          29                 //設置用戶名
          30                 ((NameCallback)callbacks[i]).setName(user);
          31             } else if (callbacks[i] instanceof PasswordCallback) {
          32                 System.out.print(((PasswordCallback)callbacks[i]).getPrompt());
          33                 String pass=(new BufferedReader(new InputStreamReader(System.in))).readLine();
          34                 //設置密碼
          35                 ((PasswordCallback)callbacks[i]).setPassword(pass.toCharArray());
          36             } else {
          37                 throw(new UnsupportedCallbackException(
          38                             callbacks[i], "Callback class not supported"));
          39             }
          40         }
          41     }
          42 }
          43 

          C:
          接下來,loginContext會 回調依次調用 LoginModule的 方法, 次序如下:
          LoginModule.login
          LoginModule.commit

          如果commit返回 false,則會調用 LoginModule.abort方法
          看一下RdbmsLoginModule類的實現:
            1 public class RdbmsLoginModule implements LoginModule {
            2 
            3     // initial state
            4     CallbackHandler callbackHandler;
            5     Subject  subject;
            6     Map      sharedState;
            7     Map      options;
            8 
            9     // temporary state
           10     Vector   tempCredentials;
           11     Vector   tempPrincipals;
           12 
           13     // the authentication status
           14     boolean  success;
           15 
           16     // configurable options
           17     boolean  debug;
           18     String   url;
           19     String   driverClass;
           20 
           21     /**
           22      * <p>Creates a login module that can authenticate against
           23      * a JDBC datasource.
           24      */
           25     public RdbmsLoginModule() {
           26         tempCredentials = new Vector();
           27         tempPrincipals  = new Vector();
           28         success = false;
           29         debug   = false;
           30     }
           31 
           32     /**
           33      * Initialize this <code>LoginModule</code>.
           34      *
           35      * <p>
           36      *
           37      * @param subject the <code>Subject</code> to be authenticated. <p>
           38      *
           39      * @param callbackHandler a <code>CallbackHandler</code> for communicating
           40      *            with the end user (prompting for usernames and
           41      *            passwords, for example). <p>
           42      *
           43      * @param sharedState shared <code>LoginModule</code> state. <p>
           44      *
           45      * @param options options specified in the login
           46      *            <code>Configuration</code> for this particular
           47      *            <code>LoginModule</code>.
           48      */
           49     public void initialize(Subject subject, CallbackHandler callbackHandler,
           50             Map sharedState, Map options) {
           51 
           52         // save the initial state
           53         this.callbackHandler = callbackHandler;
           54         this.subject     = subject;
           55         this.sharedState = sharedState;
           56         this.options     = options;
           57 
           58         // initialize any configured options
           59         if (options.containsKey("debug"))
           60             debug = "true".equalsIgnoreCase((String)options.get("debug"));
           61 
           62         url          = (String)options.get("url");
           63         driverClass  = (String)options.get("driver");
           64 
           65         if (debug) {
           66             System.out.println("\t\t[RdbmsLoginModule] initialize");
           67             System.out.println("\t\t[RdbmsLoginModule] url: " + url);
           68             System.out.println("\t\t[RdbmsLoginModule] driver: " + driverClass);
           69         }
           70     }
           71 
           72     /**
           73      * <p> Verify the password against the relevant JDBC datasource.
           74      *
           75      * @return true always, since this <code>LoginModule</code>
           76      *      should not be ignored.
           77      *
           78      * @exception FailedLoginException if the authentication fails. <p>
           79      *
           80      * @exception LoginException if this <code>LoginModule</code>
           81      *      is unable to perform the authentication.
           82      */
           83     public boolean login() throws LoginException {
           84 
           85         if (debug)
           86             System.out.println("\t\t[RdbmsLoginModule] login");
           87 
           88         if (callbackHandler == null)
           89             throw new LoginException("Error: no CallbackHandler available " +
           90                     "to garner authentication information from the user");
           91 
           92         try {
           93             // Setup default callback handlers.
           94             Callback[] callbacks = new Callback[] {
           95                 new NameCallback("Username: "),
           96                 new PasswordCallback("Password: "false)
           97             };
           98 
           99             callbackHandler.handle(callbacks);
          100 
          101             String username = ((NameCallback)callbacks[0]).getName();
          102             String password = new String(((PasswordCallback)callbacks[1]).getPassword());
          103 
          104             ((PasswordCallback)callbacks[1]).clearPassword();
          105 
          106             success = rdbmsValidate(username, password);
          107 
          108             callbacks[0= null;
          109             callbacks[1= null;
          110 
          111             if (!success)
          112                 throw new LoginException("Authentication failed: Password does not match");
          113 
          114             return(true);
          115         } catch (LoginException ex) {
          116             throw ex;
          117         } catch (Exception ex) {
          118             success = false;
          119             throw new LoginException(ex.getMessage());
          120         }
          121     }
          122 
          123     /**
          124      * Abstract method to commit the authentication process (phase 2).
          125      *
          126      * <p> This method is called if the LoginContext's
          127      * overall authentication succeeded
          128      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
          129      * succeeded).
          130      *
          131      * <p> If this LoginModule's own authentication attempt
          132      * succeeded (checked by retrieving the private state saved by the
          133      * <code>login</code> method), then this method associates a
          134      * <code>RdbmsPrincipal</code>
          135      * with the <code>Subject</code> located in the
          136      * <code>LoginModule</code>.  If this LoginModule's own
          137      * authentication attempted failed, then this method removes
          138      * any state that was originally saved.
          139      *
          140      * <p>
          141      *
          142      * @exception LoginException if the commit fails
          143      *
          144      * @return true if this LoginModule's own login and commit
          145      *      attempts succeeded, or false otherwise.
          146      */
          147     public boolean commit() throws LoginException {
          148 
          149         if (debug)
          150             System.out.println("\t\t[RdbmsLoginModule] commit");
          151 
          152         if (success) {
          153 
          154             if (subject.isReadOnly()) {
          155                 throw new LoginException ("Subject is Readonly");
          156             }
          157 
          158             try {
          159                 Iterator it = tempPrincipals.iterator();
          160                 
          161                 if (debug) {
          162                     while (it.hasNext())
          163                         System.out.println("\t\t[RdbmsLoginModule] Principal: " + it.next().toString());
          164                 }
          165 
          166                 subject.getPrincipals().addAll(tempPrincipals);
          167                 subject.getPublicCredentials().addAll(tempCredentials);
          168 
          169                 tempPrincipals.clear();
          170                 tempCredentials.clear();
          171 
          172                 if(callbackHandler instanceof PassiveCallbackHandler)
          173                     ((PassiveCallbackHandler)callbackHandler).clearPassword();
          174 
          175                 return(true);
          176             } catch (Exception ex) {
          177                 ex.printStackTrace(System.out);
          178                 throw new LoginException(ex.getMessage());
          179             }
          180         } else {
          181             tempPrincipals.clear();
          182             tempCredentials.clear();
          183             return(true);
          184         }
          185     }
          186 
          187     /**
          188      * <p> This method is called if the LoginContext's
          189      * overall authentication failed.
          190      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
          191      * did not succeed).
          192      *
          193      * <p> If this LoginModule's own authentication attempt
          194      * succeeded (checked by retrieving the private state saved by the
          195      * <code>login</code> and <code>commit</code> methods),
          196      * then this method cleans up any state that was originally saved.
          197      *
          198      * <p>
          199      *
          200      * @exception LoginException if the abort fails.
          201      *
          202      * @return false if this LoginModule's own login and/or commit attempts
          203      *     failed, and true otherwise.
          204      */
          205     public boolean abort() throws javax.security.auth.login.LoginException {
          206 
          207         if (debug)
          208             System.out.println("\t\t[RdbmsLoginModule] abort");
          209 
          210         // Clean out state
          211         success = false;
          212 
          213         tempPrincipals.clear();
          214         tempCredentials.clear();
          215 
          216         if (callbackHandler instanceof PassiveCallbackHandler)
          217             ((PassiveCallbackHandler)callbackHandler).clearPassword();
          218 
          219         logout();
          220 
          221         return(true);
          222     }
          223 
          224     /**
          225      * Logout a user.
          226      *
          227      * <p> This method removes the Principals
          228      * that were added by the <code>commit</code> method.
          229      *
          230      * <p>
          231      *
          232      * @exception LoginException if the logout fails.
          233      *
          234      * @return true in all cases since this <code>LoginModule</code>
          235      *        should not be ignored.
          236      */
          237     public boolean logout() throws javax.security.auth.login.LoginException {
          238 
          239         if (debug)
          240             System.out.println("\t\t[RdbmsLoginModule] logout");
          241 
          242         tempPrincipals.clear();
          243         tempCredentials.clear();
          244 
          245         if (callbackHandler instanceof PassiveCallbackHandler)
          246             ((PassiveCallbackHandler)callbackHandler).clearPassword();
          247 
          248         // remove the principals the login module added
          249         Iterator it = subject.getPrincipals(RdbmsPrincipal.class).iterator();
          250         while (it.hasNext()) {
          251             RdbmsPrincipal p = (RdbmsPrincipal)it.next();
          252             if(debug)
          253                 System.out.println("\t\t[RdbmsLoginModule] removing principal "+p.toString());
          254             subject.getPrincipals().remove(p);
          255         }
          256 
          257         // remove the credentials the login module added
          258         it = subject.getPublicCredentials(RdbmsCredential.class).iterator();
          259         while (it.hasNext()) {
          260             RdbmsCredential c = (RdbmsCredential)it.next();
          261             if(debug)
          262                 System.out.println("\t\t[RdbmsLoginModule] removing credential "+c.toString());
          263             subject.getPrincipals().remove(c);
          264         }
          265 
          266         return(true);
          267     }
          268 
          269     /**
          270      * Validate the given user and password against the JDBC datasource.
          271      * <p>
          272      *
          273      * @param user the username to be authenticated. <p>
          274      * @param pass the password to be authenticated. <p>
          275      * @exception Exception if the validation fails.
          276      */
          277     private boolean rdbmsValidate(String user, String pass) throws Exception {
          278         
          279         Connection con;
          280         String query = "SELECT * FROM USER_AUTH where userid='" + user + "'";
          281         Statement stmt;
          282         RdbmsPrincipal  p = null;
          283         RdbmsCredential c = null;
          284         boolean passwordMatch = false;
          285 
          286         try {
          287             Class.forName(driverClass);
          288         }
          289         catch (java.lang.ClassNotFoundException e) {
          290             System.err.print("ClassNotFoundException: ");
          291             System.err.println(e.getMessage());
          292             throw new LoginException("Database driver class not found: " + driverClass);
          293         }
          294 
          295         try {
          296             if (debug)
          297                 System.out.println("\t\t[RdbmsLoginModule] Trying to connect");
          298 
          299             con = DriverManager.getConnection(url, "Administrator""");
          300 
          301             if (debug)
          302                 System.out.println("\t\t[RdbmsLoginModule] connected!");
          303 
          304             stmt = con.createStatement();
          305 
          306             if (debug)
          307                 System.out.println("\t\t[RdbmsLoginModule] "+query);
          308 
          309             ResultSet result  = stmt.executeQuery(query);
          310             String dbPassword = null, dbFname = null, dbLname = null;
          311             String updatePerm = null, deletePerm = null;
          312             boolean isEqual   = false;
          313 
          314             while (result.next()) {
          315                 if (!result.isFirst()) 
          316                     throw new LoginException("Ambiguous user (located more than once): "+user);
          317                 dbPassword = result.getString(result.findColumn("password"));
          318                 dbFname    = result.getString(result.findColumn("first_name"));
          319                 dbLname    = result.getString(result.findColumn("last_name"));
          320                 deletePerm = result.getString(result.findColumn("delete_perm"));
          321                 updatePerm = result.getString(result.findColumn("update_perm"));
          322             }
          323 
          324             if (dbPassword == null)
          325                 throw new LoginException("User " + user + " not found");
          326 
          327             if (debug)
          328                 System.out.println("\t\t[RdbmsLoginModule] '"+pass + "' equals '" + dbPassword + "'?");
          329 
          330             passwordMatch = pass.equals(dbPassword);
          331             if (passwordMatch) {
          332                 if (debug) 
          333                     System.out.println("\t\t[RdbmsLoginModule] passwords match!");
          334 
          335                 c = new RdbmsCredential();
          336                 c.setProperty("delete_perm", deletePerm);
          337                 c.setProperty("update_perm", updatePerm);
          338                 this.tempCredentials.add(c);
          339                 this.tempPrincipals.add(new RdbmsPrincipal(dbFname + " " + dbLname));
          340             } else {
          341                 if (debug)
          342                     System.out.println("\t\t[RdbmsLoginModule] passwords do NOT match!");
          343             }
          344             stmt.close();
          345             con.close();
          346         }
          347         catch (SQLException ex) {
          348             System.err.print("SQLException: ");
          349             System.err.println(ex.getMessage());
          350             throw new LoginException("SQLException: "+ex.getMessage());
          351         }
          352         return(passwordMatch);
          353     }
          354 }
          355 

          Good Luck!
          Yours Matthew!
          posted on 2008-05-28 09:27 x.matthew 閱讀(2687) 評論(2)  編輯  收藏 所屬分類: Spring|Hibernate|Other framework
          主站蜘蛛池模板: 揭阳市| 内黄县| 德惠市| 汽车| 伊通| 大渡口区| 丰顺县| 乌审旗| 永定县| 若尔盖县| 兴山县| 颍上县| 凤山县| 泾阳县| 中卫市| 南开区| 安福县| 凌源市| 靖安县| 斗六市| 卓尼县| 万荣县| 乌恰县| 景洪市| 来凤县| 姜堰市| 白河县| 龙口市| 得荣县| 修武县| 临漳县| 高邮市| 大竹县| 北宁市| 汉源县| 津南区| 马边| 清徐县| 嘉禾县| 青川县| 千阳县|