Security2 1|学习笔记

简介: 快速学习 Security2 1

开发者学堂课程【高校精品课-上海交通大学-企业级应用体系架构:Security2 1】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/75/detail/15825


Security2 1

 

内容介绍

一、举例

二、讲述案例:注册

三、login in modules 的写法

四、代码

五、解决问题

 

一、举例

如果在 GetPropost.policy 代码中删除 OS(GetPropost.polley 的第二行代码)的属性,GetPropost.policy 的运行结果将会报错

GetPropost.policy:

grant {

Permission java.util.PropertyPermission"java.version","read”;

Permission java.util.PropertyPermission"01.name","read”;

Permission java.util.PrepertyPermission " java. home"", "read"

}

对于 GetPropost.java 的代码是依次进行读取

先读取 java 的版本->操作系统-> java.home,由于操作系统在读取时已经抛出异常(抛出异常:意味着正常的流程不执行),则会被 catch,catch 后会输出 Caught exception 异常打印出下图标红字体的第二行。所以尽管允许读取 java.home,但由于程序终止,所以并未读取 java.home。

若删除 GetPropost.policy 的第一行代码,则无法读取出任何属性,可观察到读取至 java.version,直接报出异常显示无法读取 java.version ,按照流程可以观察到在第一行代码就出现了问题,将会到 catch{} 代码,所以之后的代码也无法读取之所以无法读取代码受到权限控制,但之前学习的版本在运行过程中可能代码并无问题,而是执行出现问题

点击Getpript->Save->Application->Getpropt

注意:下图中的等号。==(俩个等号)只用自己自定义的 policy,一个=(等号)想用自定义的 policy 叠加系统的 policy ,而系统的 policy 允许用户读取 jar.home,所以如果将自定义的 policy 注释后仍然会出现

 

二、讲述案例:注册

1.将课件里面描述运行该例的命令下述命令),复制到运行的配置中,让它工作的目录为 out/product 目录

java -classpath login.jaraction.jar-

Djava.security.policy=lAASTest.policy-

Djava.security.auth.login.config=Jaas.config AuthTest

点击运行代码,运行代码后会显示认证成功,运行成功后将允许读取

运行结果:

Authentication suCessful

Subject=主体:  

//主体自带俩个 Principal ,分别为 Username=harry,role=admin

主用户:SimplePrincipal@635a0cd9

主用户:SimplePrincipal@31d27I20

username=harry

role=admin

2.AuthTest.java 中代码的含义是:

(1)先预设好用户名为 harry,密码为 secret使用 name=”login1” 的配置加载 login in modules

(2)Login1

{SimpleLogin.Module required pwfile="password.txt";}

Harry|secret|admin

Carl|buessme|HR

login1 在 Jass.config定义

此次认证必须使 SimpleLogin.Module。SimpleLogin.Module 自带 required 属性必须要通过),如果通过,则该次认证失败。

带有参数 pwfile="password.txt" 意为在同一个目录password.txt下,之前课程讲述的用|线分隔开的三列,第一列用户名第二列是密码,第三列是角色。

该代码使用 SimpleLogin.Module 进行认证,

pwfile="password.txt"作为数会传递给 SimpleLogin.Module。 

(3)创建好 loginContestlogin上下文,调login。

上个课程所画的图:

已经在 Login-Config 中创建好 loginContest就得知是使用 Login1加载的 LoginModule在调用 Login时,loginContest 将会调用 LoginModule 来调用 login

可在 AuthTest.java 中观察到在创建 loginContest 后,有第二个参数 SimpleCallbackHandler.

SimpleCallbackHandler 的作用:

New SimpleCallbackHandler(username, password )是我们所定义的创建一个对象,上个课程讲述别人的 login 用来输入东西,所以给用户了一些回调对象的 callbacks()用户要用 callbacksHandler去处理对象,callbacksHandler需要被LoginModule 用到。LoginModule 要用给它的 callbacksHandler 处理希望用户填入的callbacks()。

(4)Context.login()(LoginModule后如果成功认证就可看到结果出现 Authentication successful

要将 Subject subject = context.getSubject() 取出,取出后发现 subject 中有对象后可将 subject 中的 Principals 全部取出(因为经过认证后,已经带有了一些 Principals )为了演示输入输出所以应新创建名为admin的 UninPrincipals (将Principals的数据取出后)将新的元素追加进 UninPrincipals 中。将下述图示的代码删除也可(演示所用)。

image.png

(5)特征动作:

image.png

创建一个特征动作 SysProAction,给该特征动作传入了属性,而特征动作在 SysProAction.java 中被定义,特征动作继承自 PrivilegedAction。

在代码 public Object run() 中 run 的作用是读取构造器传入的系统属性所以在 AuthTest,java 中构造 SysProAction 时传入了“OS.name”,所以 run 将“OS.name”传递特征动作,实际上是我们获取系统的 OS 属性。

定义SysProAction:

image.png

(6)在 Object result=Subject.doAsPrivileged

(subject,action,AccessCentroller.getcontext()) 中上述认证过 subject 执行 action(要使用 AccessCentroller.getcontext())

执行 action 时是否有权限就要了解到该代码如何授权授权时只有 principalClass 的 simpleprincipl”role=admin” 的制定程序的线程的 subject,才能够允许读取系统属性。

所以 AccessCentroller 是检查subject是否有 principl 观察是否能执行 action当前的问题是登录的模块是如何 principl 给它入 subject 中。

 

三、login in modules 的写法

login in modules 在 js.consist 文件中可以写多个(但上述文件只描述了一个),但在 jass.configa 文件中可写多个 login in modules,在认证时会依次调用每个 login in modules 方法。

1.The following modules are supplied in the

com.sun.security.auth.module package:

-UnixLoginModule

-NTLoginModule

- Krb5LoginModule

- JndiLoginModule

- KeyStoreLoginModule

A login policy consists of a sequence of login modules, each of which is labeled required, sufficient, requisite, or optional:

- The modules are executed in turn, until a sufficient module succeeds, a

requisite module fails, or the end of the module list is reached.

- Authentication is successful if all required and requisite modules

succeed, or if none of them were executed, if at least one sufficient oroptional module succeeds.

例如:在 jass 中 login in modules 的配置有多个模块,将依次去调用

(1)每个模块后都有 required, sufficient, requisite, optional,意为是在java的安全体系中有俩个约定

首先按顺序执行,如果 login in modules 的配置有上述模块构成且按顺序执行,如果有一个 sufficient 模块成功,那么该事件工作(有五个模块,假如第二个模块sufficient(充分)成功,则后续不需要操作);如果某一个requisite模块失败, 则下述也不需操作,因为配置模块必须成功。

如果所有的属性都为required ,则会一直执行到底,在每个模块执行时,都会给一个标记,说明该模块成功还是失败(模块一:成功;模块二:成功;模块三:失败)注意如果失败,对于required 模块而言不会立即结束。(模块四:成功;模块五:成功),全部成功之后,再次观察发现前俩个模块成功,第三个模块失败最后两个模块成功总体来说有一个required 模块失败,所以整个认证失败上述即为我们所讲述的需要所有 required 模块和 requisite 模块都必须成功。 

(2)复杂原因

假设5个模块

UnixLoginModule     //常规的用户名密码

Retinon      //扫描视网膜

fp         //指纹

Voice    //声音

用户在认证时,可能在指纹失败后会去洗手拼命搓手,想办法过原因是因为知道指纹失败

如果并不想让人知道指纹失败,方法是把模块全部变为required 在过指纹失败后继续往下,所有都执行结束后,最后结果显示失败,因此用户并不了解在那个环节失败。因此可以使用 required 。

使用sufficient 意味视网膜被仿造的可能性很低,只要视网膜成功,其他模块不需要检验就可跳过

指纹失败后需要验练声音的原因是不想让用户知道是指纹失败而导致失败,但如果考虑到指纹失败的概率较大的原因是指纹可能不干净,就可将指纹设置为 optional(可选)。如果成功,其他模块也无问题(成功)所以对指纹是否成功没有要求,但如果其他模块不成功,并且其他模块也都为 optional,但是指纹成功则会用户通过

因为上述所讲的细节比较复杂。所以礼拜一的课堂中它为可堆栈可插拔。

可插拔:在任何一个 login in 配置文件里,随便去掉一个模块或加上一个模块都可以将模块一起之后,可用不同的属性去定义定义出的效果会不同 

(3)上述所讲,可以了解到依赖配置文件来实现并未依赖代码所以Java 在很多程序框架类似上述的途径。之后讲述事物可了解到事物属性也是途径。不仅 java 的程序,写其他任何程序都应该达到不改源码的情况下,是否能够自动识别的效果

回想在 spring 中 inotation 时讲 Autowried 的效果相同Autowried 变量的类型是 Interface 接口类型,spring能找到一个实现类注入进 Autowried 里的原因是:因为它有一系列的规则。例如:它帮助寻找非常相近的名

注解到接口类型的原因:如果将来Interface接口换一个实现,代码不需要修改新的实现替换原来的实现,但使用实现的代码无需修改,因为这些代码接口变成在OOAD 分析设计里讲述,所有的东西应该依赖于稳定的东西,不稳定依赖于稳定,代码容易做配置修改),所以尽量不要修改源码,程序可以通过配置的方式实现达到同样的效果。

2.特征动作:上述所讲的实现 PrivilegedAction 扩展的类(自己写),在此处老师写的为命名类

下述代码是为了让同学们了解 principal 的过程,也可删去。

grant principalClass "principalName"

PrivilegedAction action = new

PrivilegedAction()

{

public Object run(){

{

// run with permissions of subject principals;具体逻辑

...

}

};

Subject.doAsPrivileged(subject,action,null);

//or doAs(subject,action);重点是在执行动作中,必须要认证之后的 subject 执行

3. 执行逻辑:

AuthTest.java:

//学习上述逻辑:

import java.security.*;

import javax.security.auth.*;

import javax.security.auth.login.*;

public class AuthTest

{

public static void main(final String[]args)

{

Try

{

System.setSecurityManager(new SecurityManagerO); 

LoginContext context = new LoginContext("Login1");

context.login();

System.out.println("Authentication successful.";

//安装安全管理器后再 Login1 在 Jaas.config中配置的认证模块进行登录登录成功后会出来下述结果

Subject subject = context.getSubject(); //获得 subject

System.out.println("subject=" + subject);

PrivilegedAction action = new SysPropAction("user.home");

Object result=Subject.doAsPrivileged(subject,action,null);

//用 subject 执行特征动作

System.out.println(result);

context.logout();

}

catch(LoginException e)

{

e.printStackTrace();}

}

}

}

4.SysProAction.java

//在讲述完简单的类之后,我们学习 login in modules 里的代码

import java.security.*;

public class SysPropAction implements PrivilegedAction

//实现特征动作的接口

{

/**

Constructs an action for looking up a given property.

@param propertyName the property name (such as "userhome")

*/

public SysPropAction(String propertyName)

{ this.propertyName = propertyName;}

public Object run()  

//代码很简单,获取系统属性,属性是从 SysPropAction()中传递

{

return System.getProperty(propertyName);

}

private String propertyName;

}

5.AuthTest.policy

grant codebase "file:login.jar"

//在运行时一部分代码是 login 的 Authtest 类,权限是能够创建LoginContext 和执行特征动作(doAsPrivileged)。

{

Permission javax.security.Auth.AuthPermission

"createLoginContext.Login1";

permission javax.security.auth.AuthPermission

"doAsPrivileged";

}; 

//在真正执行动作时使用 principal 授权(必须有某一种principal),之后的代码都是自定义

grant principal com.sun.security.auth.UnixPrincipal "harry"

{

permission java.util.PropertyPermission "user.*" , "read";

} 

- Jaas.config    //执行代码依据 config

Login1

{

com.sun.security.auth.module.UnixLoginModule required;

//使用某一个用户名密码是必须要实现可观察到在实际的程序中在此处加入了自定义 LoginModule 的 pwfile="password.txt" 参数

Login1{SimpleLoginModule required pwfile="password.txt";};并未使用此处代码,此处代码讲述的是一般写法

}; 

6.举例

(1)javac AuthTest.java

jar cvf login.jar AuthTest*.class

javac SysPropAction.java

jar cvf action.jar SysPropAction.class

java -classpath login.jar:action.jar

-Djava.security.policy= AuthTest.policy

-Djava.security.auth.login.config=jaas.config

AuthTest

AuthTest.java 压缩成 login.jar对照 login.jar 授权 file:login.jar此处有创建动作,将特征动作压缩成特征动作的 .jar 包。

执行特征动作,所有的类都在 login.jar:action.jar 中寻找,并且使用自己编写的 AuthTest.policy 以及用系统之前定义的 LoginModulejaas.config 最终运行 AuthTest 类。

(2)we can add role-based permissions into a policy file:

grant principal SimplePrincipal "role=admin" {..}

SimplePrincipal  包括 SimplePrincipal login modulecodebase上述所学的授权是针对 principal执行代码的线程观察subject 的principal ,必须具备 SimplePrincipal SimplePrincipal 的值为role=admin

(3)Our login module looks up users, passwords, and roles in a text file that contains lines like this:

harry|secret|admin

Carl|guessme|HR

密码为 harry|secret|admin

Carl|guessme|HR 文件中为三列。

(4)The Login Module checks whether the user name and password match a user record in the password file. If so, we add two SimplePrincipal objects to the subject's principal set:

Set<Principal> principals = subject.getPrincipalsQ;

principals.add(new SimplePrincipal("username", username]);

principals.add(new SimplePrincipal(C" role", role]);

期望当 subject 在经过 Login Module 认证成功后,希望把它具有的 principal集合里添加两个新的 principal是上述代码时所学的 username=harry 和 role=admin;之后将会学习上述三行代码应该放至何处。

7.An Example: SimpleLoginModule.

(1)The initialize method receives

The Subject that is being authenticated.

A handler to retrieve login information.

A sharedState map that can be used for communication between login

modules.

An options map that contains name/value pairs that are set in the login configuration.

编写 SimpleLoginModule,在 jass.config 中指定,SimpleLoginModule并且为 requird(必须成功),带有参数 pwfile。

在 SimpleLoginModule 中是扩展的 LoginModule 的类,类中有 initialize 的方法,该方法能够接收下述参数

①Subject :在认证的时候,实际上可能会有多种模块。所以在开始执行代码时 context.login() 时系统已经组装了subject 对象,subject 对象开始Principal 集合是空,没有任 何Principal,之后 subject会被做参数之一传递给第一个模块,第一个模块在认证后会在 subject 里放一些 Principal并把它当成参数再次传递给第二个模块依次类推。所以我们现在所观察到的 initialize 里的第一个参数subject线程对象。不管之前的 subject 如何,当前得到了一个 subjectsubject 里可能已经带一些 Principal 或者该 subjec t是第一个认证模块( Principal的模块)总之得到 subject 的目的 subject 进行操作,subject 就是我要认证的对象

②Handler:

上述所讲的LoginModule 需要把一些 callback 发送给客户端,客户端要过填入信息之后返回,客户端决定用哪个 handler 处理callback在本课上述所举的例子中:例如给了一个盘子,让你放入身份证,然后他给你办机票用镊子夹身份证还是拿手拿身份证都取决于我。因为 LoginModule 对象给 CallbackHandlerCallbackHandler 调用该对象得到处理 callbacks 的结果

LoginModule 如何将对象传给 CallbackHandler:(注意观察代码打开Auth.java,在创建LoginContext时,有第二参数New SimpleCallbackhandler(username, password),即可得到参数

所以客户端创建的对象传递给了 Login 方法,然后 ContextLogin方法会依次传递给每一个 LoginModule 

③sharedState :

上述所谈到的问题(五个模块中前俩个和后俩个成功,第三个失败)

这些信息之间互相如何通信?如果不知道如何通信,就办法确定整个过程到底是成功还失败。比如说第三个模块 required 失败,整个过程失败,即使每个模块与其他4个模块将该用户认证成功不会添加任何 Principal只要有一个失败,不会有任何 PrincipaL),类似于一个人机场,如果最终上飞机,不应该保留登记牌东西,会被全部收回。所以每个模块互相之间需要有一个状态的沟通

所有模块有一个共同的 map 表,每个模块都可 map 中输入,相当于 LoginModule 之间公共的对象,每个模块可以从 map 表里得到信息

④options :

是键值对,出现于 login configuration 中所以现在就了解了密码文件如何入。jass.config 中可观察到有键值对。pwfile=”password.txt”,pwfile=”password.txt”是 LoginModule的options 。

(2)For example, we configure our module as follows:

SimpleLoginModule required pwfile=" password.txt";

即为 jass.config 文件中的代码。

(3)The login module retrieves the pwfile settings from the options map.

(4)The handler is specified when you construct the LoginContext

(5)For example,

LoginContext context = new LoginContext("Login1", new

com.sun.security.auth.callback.DialogCallbackHandlerO);//第二个参数

到目前为止我们将完了四个参数为pwfile=”password.txt”,第三个参数是每个模块所共有的。第二参数是在客户端创建LoginContext时所传递进的,第一个参数是系统帮组装的。

initialize 类似于构造器刚开始(创建出之后)会被调用 

8.handle 方法:

An Example: SimpleLoginModule.

(1)The handle method of handlers

public void handle(Callback[] callbacks)

{for (Callback callback : callbacks) {

if (callback instanceof NameCallback) ...

else if (callback instanceof PasswordCallback)

...else . . .

}

}

handle 方法是:

LoginModule 希望得到用户名和密码,所以创建两个 Callbackhandle(Callback[] callbacks),传递给 handle方法。handler 里的 handle 方法是 handle(Callback[] callbacks)callbacks 传递给 handle(Callback[] callbacks) 时,handle(Callback[] callbacks) 就用handle 去处理DialogCallbackHandler() 是客户端指定handler。客户端也可指定其他的 handler,无论如何都需有一个 handle 方法,而在 LoginModule 里,不管是什么,只要有handle 方法,就调用 handle 方法,把要传递进去的 Callbackhandle 传递进去以往 LoginModule 里组装,从内存里填写或让用户输入或者是读数据库 handler 要去解决的问题。

(2)Prepare Callbacks for handler

NameCallback nameCall = new NameCallback("username: ");

PasswordCallback passCall = new PasswordCallback("password: ",false);

callbackHandler.handle(new Callback[] { nameCall, passCall });

所以 LoginModule 创建 callback,比如需要 new Callback()PasswordCallback()都是 Callback 的子类,然后就调用刚才从参数得到的 handler 的对象的 handle 方法,然后把两个 Callback 放到一个 Callback 数组里传递于是{ nameCall, passCall }中将会被填入数据

 

四、代码

1.SimbleLoginModule.java

import java.io.*;

import java.lang.reflect.*;

import java.security.*;

import java.util.*;

import javax.security.auth.*;

import javax.security.auth.login.*;

import javax.security.auth.callback.*;

import javax.security.auth.spi.*;

import javax.swing.*;

/**

This login module authenticates users by reading

usernames,passwords,and roles from a text file.

*/

public class SimpleLoginModule implements LoginModule

//观察完整的 Login 模块,SimpleLoginModule  Login 模块,必须要实现 LoginModule 接口

-SimpleLoginModule.java

public void initialize(Subject subject,

CallbackHandler callbackHandler,

Map<String,?> sharedState,Map<String,?> options)

{

this.subject = subject;

this.callbackHandler'= callbackHandler;

this.sharedState = sharedState;

this.options = options;

}

//使用 initialize传递了四个方法subject,客户端发送的callbackHandler,多个模块互相分享状态 sharedState 的 map 对象,在 jass 配置文件里的 options 该方法本身并未有什么作用,只是将四个参数赋予给当前的四个元素

public boolean login() throws LoginException

if (callbackHandler == null)

throw new LoginException("no handler");

NameCallback nameCall = new NameCallback("username: ");

PasswordCallback passCall = new PasswordCallback("password:" ,false);

try

{

callbackHandler.handle(new Callback[ { nameCall, passCall });

}

// login() 方法,对应有 logout() 方法,login() 方法线判断是否传入 callbackHandler ,如果没有传入,则将无法验证;如果有callbackHandler ,则组装一个 NameCallbackPasswordCallback,这俩个 Callback 刚开始是空的,将俩个 Callback 一个 Callback 数组里调用 Handle 方法,调用结束之后,{ nameCall, passCall }将会带有内容,后续要对内容进行处理。 

catch (UnsupportedCallbackException e)

{

LoginException e2 = new LoginException("Unsupported callback");

e2.initCause(e);

throw e2;

}

catch (IOException e)

{

LoginException e2 = new LoginException("I/0 exception in callback");

e2.initCause(e);

throw e2;

}

return checkLogin(nameCall.getName(), passCall.getPassword());

}

//调用自定义 checkLogin(),验证 callbackHandler 处理完之后的俩个 callback 里的 Name 和 Password 是否是我们所希望得到得内容。该逻辑取决于下述代码 

/**

Checks whether the authentication information is valid. lf it is, the subject acquires principals for the user name and role.

@param username the user name

@param password a character array containing the password

@return true if the authentication information is valid

*./

private boolean checkLogin(String username,char[]password) throws LoginException

{

try

{

Scanner in = new Scanner(new FileReadér("" + options.get("pwfile"));

while (in.hasNextLine())

{

String[] inputs = in.nextLine(.split("\\|");

if (inputs[0].equals(username) &&Arrays.equals(inputs[1].toCharArray), password))

{ String role = inputs[2];

Set<Principal> principals = subject.getPrincipals();

principals.addnew SimplePrincipal("username", username));

principals.add(new SimplePrincipal("role" ,role));

return true;

}

}

//读取 options 中的 pwfile=”password.txt” 的属性信息,pwfile的键值对可以是多个,所以就会将 password.txt 文件取出;while (in.hasNextLine() 代表不断地遍历每一行,用|将每一行信息分隔开,观察 username 和 inputs[0].equals() 是否相同,password 和 inputs[1].toCharArray() 是否相同,如果相同则就认为该用户是合法用户,他的角色取出,然后获取 subject  Principal 集合

//如果是第一个模块,那么系统组装的 subject 为空(没有任何东西);如果它是中间的某一个模块,前面已经有一些模块认证过,subject 里现在也是空的,但是被认证过的模块都记住自己是成功还是失败,所以有可能被认证过的模块会在 subject 中放入一些数据(稍后解释)。取出 subject  Principals 集合,然后往里面放两个 SimplePrincipal(username和role),当前的模块经过他的认证带带有 username 和 role 两个属性Principal

如果说用户名密码符合要求,否则该段代码不执行,所以认证通过就会有这 username 和 role 的 Principal(在通过之后 loginmodule 会给 subject上添加了两个Principal)

in.close();

return false;

}

catch (IOException e)

{

LoginException e2 = new LoginException("Can't open password file");

e2.initCause(e);

throw e2;

}

public boolean logout(){ return true; }

public boolean abort(){ return true; }

public boolean commit(){ return true; }

private Subject subject;

private CallbackHandler callbackHandler;

private Map<String,?>sharedState;

private Map<String,?> options;

}

//在代码中有 logout() 是登出,abort(),commit();三个代码都较为简单,直接返回 true

Subject,callbackHandler.sharedState,optionsoptions处理的4个参数成员变量

2.SimplePrincipal.java

import java.security.*;

/**

A principal with a named value (such as "role=HR"or"username=harry").

*/

public class SimplePrincipal implements Principal

{

/**

Constructs a SimplePrincipal to hold a description and a value.

@param roleName the role name

*/

public SimplePrincipal(String descr,String value)

{this.descr = descr; this.value = value;

}

/**

Returns the role name of this principa

@return the role name

*/

//在完整体系中有 Principal 接口可直接实践,在处理 Principal的时,可回想起在上述创建 Principal 时传入了俩个参数两个字符串,无论哪个Principal都是两个字串,第一个(“username”,username)username 和它的值,第一个(“role”,role)role 和它的值。所以 public SimplePrincipal(String descr,String value)descr 是参数,value 是值。在传递的时候就应记住这俩个成员变量的值。 

public String getName(){ return descr+ "=" + value;}

public boolean equals(Object otherObject)

{

if (this == otherObject) return true;

if (otherObject == null) return false;

if (getClass()!= other0bject.getClass()] return false;

SimplePrincipal other = (SimplePrincipal) otherObject;

return getName().equals( other.getName());

public int hashCode(){ return getName().hashCode(); }

private String descr;

private String value;

}

//Principal 的 getName() 会被系统调用,获取 Principal 的名字。{ return descr+ "=" + value;} 表示将会返回一个 username=harry 的字符串,该字符串跟授权文件对应,在代码中和“role=admin”对应,所以系统会调用 Principal 的 getName() 方法返回的值跟这 java.util.PropertyPermission"*","read” 的内容比较决定用户是否拥有权限

按照内容比较两个对象是否相等,所以重载 equals 和 hashCod e方法,SimplePrincipal  简单的类

3.SimpleCallbackHandlerjava

import javax.security.auth.callback.*;

/**

This simple callback handler presents the given user name and password

public class SimpleCallbackHandler implements CallbackHandler

/**

Constructs the callback handler.

@param username the user name

@param password a character array containing the password

public SimpleCallbackHandler(String username, charD password)

this.username = username;

this.password = password; 

public void handle(Callback[] callbacks)

{

for(Callback callback:callbacks)

{

if (callback instanceof NameCallback)

{

((NameCallback) callback).setName(username);

}

else if (callback instanceof PasswordCallback)

{

((PasswordCallback ) callback).setPassword(password);

}

}

}

private String username;

private char[] password;

} 

//handler 系统有一个名为 CallbackHandler 的接口可直接实现,在上述代码创建 CallbackHandler 创建了具体的数值(username 被定义了harry和passward被定义了secret)意为在定义SimpleCallbackHandler 时有一个构造器参数,将用户名密码直接复制,handler 方法是在对比第一个是否为 ifcallback ,第二个是否为PasswordCallback,如果 NameCallback 里将当前的用户名username设置作为用户名下述都是类似将当前自身的成员属password 设置作为 PasswordCallback  password当前组装结束之后,PasswordCallback  NameCallback 里就带了 name(当时在创建 handler 传递进来用户名密码),所以 SimpleLoginModule.java代码中调用 handler ,一开始 nameCal l和 passCall 是空的,但是当调用 handle方法把 nameCal l和 passCall 传递时,nameCall 和passCal l里就带有了数据,所以才能在后续代码中 nameCall 和passCall 通过 getName() 和 getPassword() 把用户名密码提取出来校验它是否是合法用户。在写出了 Handle 后,即可执行整个代码。

4.JAASTest.policy

grant codebase "file:login.jar"

{

permission java.awt.AWTPermission"showWindowWithoutWarningBanner";

Permission javax.security.auth.AuthPermission

"createLoginContext.Login1";

permission javax.security.auth.AuthPermission"doAsPrivileged";

permission javax.security.auth.AuthPermission "modifyPrincipals";

permission java.io.FilePermission"password.txt","read";

};

grant principal SimplePrincipal "role=admin"

{

permission java.util.PropertyPermission "*","read";

};

//policy 模块要对 login 文件赋予权限,包括读取文件的权限,修改Principal 的权限(要在 Principal 中增加元素),执行特征动作,创建 LoginContext 权限,真正在执行特征动作是按照 Principal 执行(代码:grant principal SimplePrincipal "role=admin")即可执行代码。读取所有的系统属性

5.Jaas.config

Login1

{

SimpleLoginModule required pwfile="password.txt";};

//Login1 是使用自定义的 SimpleLoginModule required  是必须要成功pwfile 为参数,几部分组合起来则是全部代码。

 

五.解决问题

1.在运行代码时可在命令行运行或在右上角 Auth.Text 处右击 VM options 的代码,执行 Autho.Test  类

使用代码:java -classpath login.jaraction.jar-

Djava.security.policy=JAASTest.policy-

Djava.security.auth.login.config=Jaas.config AuthTest

在该处设置好之后就无需每次运行都设置,使用 Jaas.configJAASTest.policy执行压缩好的 action.jar 和 login.jaraction.jar

2.可观察到特性动作是要读取操作系统的名字,操作系统是Mac OS X

无法执行结果,报出类异常:

Shall 的问题可能由于 MAC 的机器 Shall 问题。如果在 Windows 上运行,可能是因为 Shall 的问题导致出错,可在 RunDebug中配置为上述所讲就能够解决该问题就好。而 Shall 超出我们讲课范围

image.png

3.该课程的目的,是在想告诉同学们整个认证的过程如何,而上述课程有俩处问题需要解决。

1问题一:

public boolean logout(){ return true; }

public boolean abort()( return true; }

public boolean commit(){ return true; }

上述三行代码描述过于简洁,实际上在 Principals.add 中不应直接添加,commit() 能够联想到 throw意为出的操作一直到 commit算数,在之前都缓存

commit() 意为在可堆栈中前两个模块成功第三个模块失败,在第一个模块认证成功在loginmodule中,当发现用户名和密码符合要求的时候,不应直接 loginmodule 里加 Principal应该等待整个过程结束后,判断整体是成功还是失败,然后再加 Principal。系统应该在每个 login 方法生成标记,让 java 虚拟机知道模块成功失败,然后在每个模块都成功的情况下才加入 Principal所以在SimpleLoginModule .java 中只要用密码匹配就加 Principal 的代码写的过于简单,如下:

String[] inputs = in.nextLine(.split("\\|");

if (inputs[0].equals(username) &&Arrays.equals(inputs[1].toCharArray), password))

{ String role = inputs[2];

Set<Principal> principals = subject.getPrincipals();

principals.addnew SimplePrincipal("username", username));

principals.add(new SimplePrincipal("role" ,role));

return true;

解决方法一:

代码不应该放在原代码中,应该打入一个标记,说明成功因为public boolean login() throws LoginException 方法并未说明告诉要将Principal 加入,只是让返回一个布尔值意为是在模块是成功失败,所以其实在上述代码所放位置应该记模块成功还是失败,所以该段代码应放到 commit() 中,commit() 是在系统整个判断这一次认证成功还是失败的时被调用commit() 被调用时才应放入上述代码,模块认证成功再放即使模块认证成功,整个流程失败,也不应放入代码因为流程失败,系统会调用 abort(),所以应将代码放入 commit()

解决方法二:(简单)

上述代码放在原处,但是在 abort() 和 logout() 中要将下述代码从代码中移除

principals.addnew SimplePrincipal("username", username));

principals.add(new SimplePrincipal("role" ,role));

在认证之后只要用户名密码匹配,在此处认证逻辑成功,则增加俩个 Principal,如果整体失败,系统调用abort()则将两个 Principal移除即可保证不会出现争议失败但已经添加 Principal;在 logout()也是如此,要将该模块的 Principal 移除 

所以可堆栈可堆叠的三个方法不应如此简单,此处我们为了示意而将代码简单化 

2问题二:

问题二和问题一(可堆栈)相关联相关联模块失败后,成功的模块不能加因为如果第三个模块失败,告知整个认证失败,但是却带了一堆 Principal,因为有4个模块认证成功不能给该用户授权,因为一旦授权虽然整个过程不成功,但带有一堆 Principal,只是失败的模块没有给他赋值,所以不符合要求,因为 login1 的目的是要经过所有的模块认证,才被认为成功,否则失败。如果按照上诉所说,应该只要有一个模块失败则该 subject 不应带有任何 Principal;但是一旦成功即添加Principal,不成功添加 Principal,最后报错失败,而最终 subject 会带有 Principal 的情况是允许的。

模块和模块之间传递成功,失败,或中间的状态想传递在此设计方案之下需要传递上述所讲即为 ShareraState 的作用。尽管我们所讲述的例子中由于我们所涉及的模块不够多的原因并未用到,我们只有当前一个模块无需在不同的Login module中通信,但不同的 Login module 之间有可能通信,只要有多个栈堆叠就需要通信可能会通信将该模块认证的状态的发给下一个,下一个可能或用到,所以这就是 ShareraState 参数的作用

观察我们写的 SimpleLoginModule.java 代码过于简单,真正的逻辑应该把4个方法logout() ,login(),abort(),commit()加在一起,类似在数据库课学的两阶段提交第一个阶段判断自己是否可以投票决定该事物提高还是回滚,

真正 commit(),或者 abort() 才决定整个过程成功还是失败,所以添加 Principal 和移除 Principal, abort()  commit() 方法必须有,而在 logout()整个用户应该清零时也应该把相应的logout() 移除完整的 Login module 结束。

相关文章
|
21天前
|
存储 安全 Java
“Spring Security 中的 Principal 是什么?
【8月更文挑战第21天】
44 0
|
4月前
|
安全 Java 数据安全/隐私保护
Security快速入门
Security快速入门
|
4月前
|
安全 Java API
【Spring Security】Spring Security 认证与授权
【Spring Security】Spring Security 认证与授权
72 0
|
存储 SQL 安全
Spring Security怎么给你授权的?
Spring Security核心功能, 认证和授权, 本章便是核心章节, 授权, 需要关注, 关注, 再关注 授权是什么?
|
自然语言处理 算法 安全
Security2.2|学习笔记
快速学习 Security2.2
113 0
Security2.2|学习笔记
|
存储 安全 Java
Security1 2|学习笔记
快速学习 Security1 2
100 0
Security1 2|学习笔记
|
存储 算法 安全
Security2 3|学习笔记
快速学习 Security2 3
71 0
Security2 3|学习笔记
|
存储 安全 算法
Spring Security系列教程22--Spring Security中的密码加密
前言 截止到现在,一一哥 已经带各位学习了很多关于Spring Security的知识点,但是Spring Security作为一个安全框架,其中必然就应该带有安全加密方面的内容,所以本篇文章,一一哥 就带各位来学习Spring Security中的密码加密机制。 Lets go! 一. 密码加密简介 1. 散列加密概述 我们开发时进行密码加密,可用的加密手段有很多,比如对称加密、非对称加密、信息摘要等。在一般的项目里,常用的就是信息摘要算法,也可以被称为散列加密函数,或者称为散列算法、哈希函数。这是一种可以从任何数据中创建数字“指纹”的方法,常用的散列函数有 MD5 消息摘要算法、安全散列
1372 1
|
安全 NoSQL Java
security和oauth2.0的整合
security和oauth2.0的整合 之前已经介绍过security的相关的介绍,现在所需要做的就是security和oauth2.0的整合,在原有的基础上我们加上一些相关的代码;代码实现如下: pom.
1532 0
|
安全 网络安全 数据安全/隐私保护