Security1 2|学习笔记

简介: 快速学习 Security1 2

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

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


Security1 2

 

内容介绍:

一、前言

二、权限的完整设置

三、运行效果

四、用户的认证

五、书写 Login Module 

 

一、前言

前面有咨询在刚刚运行的文件里面把前两内容注释掉之后也是可以访问的,在此需要解释下,因为所写的程序比较旧,在新版本里面可能显示出来有所不同,关于方面的问题会给大家进行详细解释。目前来说只有最后行的代码是可以的,之前并没有过多注意这个问题。自定义自己的权限时要注意 implies 方法。

 image.png

 

二、权限的完整设置

1.权限的设置

详细代码:

class wordcheckTextArea extends JTextArea {

public void append(String text) {

wordCheckPermission p = new wordCheckPermission(text,"insert");

SecurityManager manager = System.getSecurityManager();

if (manager != null)

manager.checkPermission(p);

super. append(text);

}

}

希望开发文本域——图形化界面,属于 Java 图形化的 swing 控制件。因为是文本域,所以里面具有 append 方法,append 方法可以把文本追加到文本域里,即可以把一些字追加到文本输入框中。文本域下有框,旁边还有发送框,两者结合相当于聊天室。文本框中表示所有的聊天内容,输入像 sex、drugs 或者 C++词汇时点击发送,正常情况下只需要把文字 append 进去即可,表示总的聊天空间;但是当追加的文字属于 sex、drugs 或 C++的时候,不允许直接插入,在进行追加时要新创建一个权限来执行 insert 的动作,text 表示要插入进去的文本。

然后需要判断系统中是否存在安全管理器,如果没有不需要进行任何操作,调用 JTextArea append 方法后直接将文本追加进去;如果系统有安全管理器,需要使用安全管理器检查权限。安全管理器检查权限的逻辑是这样的:它首先寻找当时指定的 policy 文件,之后进入到 police 文件找到相应的 permision,判断它是否隐含,若有一个隐含即可执行下去;而如果找完之后发现所有的权限都不隐含条件,权限就会抛出异常,意味着之后的几代码是不能运行的,因此 text 也就没有办法插入到文本域里。大家需要注意的是 security manager 在检查权限的时候,如果隐含条件可以直接执行下去,如果不隐含则会抛异常并中断正常的执行流程。

2.设计 action 之间的动作逻辑

详细代码:

grant {

permission wordCheckPermission "sex, drugs,C++","avoid";

};

l If p1 has action avoid and p2 has action insert, then the

target of p2 mustavoid all words in p1.

-wordcheckPermission "sex, drugs,C++","avoid"

-implies the permission

-wordCheckPermission "Mary had a little lamb", "insert"

l If p1 and p2 both have action avoid, then the word set of

p2 must contain allwords in the word set of p1.

-wordcheckPermission "sex, drugs","avoid"

-implies the permission

-wordCheckPermission "sex, drugs,C++","avoid"

l If p1 and p2 both have action insert, then the text of p1

must contain the textof p2.

-wordCheckPermission "Mary had a little lamb", "insert"

-implies the permission

-wordcheckPermission "a little lamb", "insert"

WordCheckPermission 是自定义的 permission,它是如何做到使 sex、drugs 以及 C++词汇无法插入的。

在 policy 文件里面需要写入关键字grant授予一个权限,这个权限就是 WordCheckPermission,需要 target 的就是sex、drugs 以及 C++这些词,Avoid 是它的 action 表示避免。Target 和 avoid 都是自己设定的,需要在程序里要进行处理。

设计几种 action 并确定 action 之间的动作。action 的设计包括避免(avoid)和单词的插入(insert)。设计时首先要设置逻辑,要避免(avoid)C++、sex、drugs这三个词。如果一个 action 为 insert Mary had a little lamb,里面没有任何 sex、C++、drugs 词汇,所以它允许这个动作执行,允许插入权限,隐含了这种权限。即权限是 insert 且插入进去的内容不包含 avoid 里的任何单词时表现为隐含;另外一种隐含关系也是 avoid,并且两者都是 avoid,第一个要求是不能出现 sex、C++和 drugs,而底下权限要求更强,除了 sex、drug,C++也不行,显然底下的权限更强,上面的权限更弱,所以只要满足下面的权限一定满足上面的,满足上面的不一定满足底下的;另外一种情况是两个都是insert,这个时候只允许插入给定的单词,超出给定范围的单词无法插入,但是可以插入给定字符串的子集。

3.写入动作

详细代码:

import java.security.*;

import java.util.*;

/**A permission that checks for bad words.

*/

public class wordCheckPermission extends Permission

{

/**

Constructs a word check permission

@param target a comma separated word list

@param anAction "insert" or "avoid"

*/

public wordcheckPermission(String target,string anAction)

{

super(target);

action = anAction;)

}

public string getActions() { return action; }

public boolean equals(object other)

{

if (other == nul1) return false;

if ( !getclass( ).equals(other.getClass() ) ) return false;

wordCheckPermission b = (wordCheckPermission) other;

if ( ! action.equals(b.action) ) return false;

if ( action.equals( "insert" ))

return getName( ).equals(b.getName( ));

else if ( action.equals( "avoid" ))

return badwordSet().equals(b.badwordSet());

else return false;

} 

分析完成权限隐含的逻辑后,开始写入 action。首先要继承 permission,所有的权限都来自于它,permission 扮演的实际上是公共类,需要扩展这个类。然后要有一个构造器,构造器需要两个参数,需要传进 target 和 action list,所以需要含有 String target,string anAction,目前的 action 不可能有 list,不是 avoid 就是 insert,所以只传 action即可。

调用 supe r 将 target 传入,并记住 action 是自己设定的;getaction 比较简单表示返回 action;equals 要根据内容来比较,如果为空肯定就是 false,如果不是同一个类也会返回。equals还有一个细节需要注意,如果两类在比较的时候比较的是对象,需要覆盖 equals 方法和 hashcode 方法,因为如果两对象的equals相同,它们的 h ashcode 返回应该相同。equals 千万不能写错,比如不能写成 WordCheckPermissionOther,这样会认为是 equals 的重载版本,在当调用的时候仍然不满足条件,不能调用成功,一定要覆盖版本,但是一旦覆盖版本可以看到类型是 object,需要进行类型转换,变成 WordCheckPermission。继续前一知识点的讲解,还需要比较两者的 action 是否相同,分别是 action 还是inser t 以及它们的名称等是否相同,只有内容全部一样才能返回 true,否则全部返回 false。即 insert 的 name 相同,avoid 的 badWordSet 相同,返回为 true 否则返回为 false。 

详细代码:

public int hashcode(){

return getName( ).hashcode() + action.hashcode();

}

public boolean implies(Permission other)

{

if ( ! (other instanceof wordCheckPermission)) return false;

wordCheckPermission b = (wordCheckPermission) other;

if (action.equals( "insert" ))

{

return b.action.equals( "insert" ) &&

getName( ).indexOf(b.getName()) >= 0;

}

else if ( action.equals( "avoid"))

{

以上就是 equals 的内容比较,hash 也要按内容比较,equals 相同,hash 代码相等,所以 hashcode 也按照内容进行比较。需要调用两个字符串,hashcode 的字符串被写成内容进行比较。最为关键的是 implies,implies 是覆类的方法,需要进行覆盖。它的传递也是一样的,是 permission 类型,需要先转型,转完型后尝试刚才的逻辑。如果当前权限是 insert,另外一个如果也是 insert,属于逻辑中的最后一种,下面的权限是上面权限的子集,属于它的字符串都满足。如果两个都是 insert,在当前的 target gatName,其中 Name 即为 t arget就是刚才的字符串,在字符串里面去找 b的字符串,看是否能找到。indexOf 表示正在寻找字符串,字符串出现的位置如果大于等于零,表示前面字符串里确实有后面字符串作为自字符串,如果是负值表示无法找到,如果能找到就意味着 B 的要求插入的字符串确实是当前权限字符串的一部分,返回结果为 true,否则就返回 false。

详细代码:

if (b.action.equals ( "avoid" ))

return b.badwordSet().containsAll (badwordSet());

else if (b.action.equals( "insert"))

{

for (String badword : badwordSet( ))

if (b.getName( ) .indexOf( badword) >= 0)

return false;

return true;

}

else return false;

}

else return false;

}

如果当前是 avoid 权限,另外一个也是 avoid,就是刚才所讲的第二种情况。谁 avoid 的权限多就被 avoid权限少的所隐含,不良单词集多的权限会被不良单词少的权限隐含。

把b的不良单词集拿出后查看其是否包含了当前权限的所有不良单词,如果包含了就意味着 b 的是不良单词大于或等于当前权限的不良单词,所以返回 true,否则返回 false。最关心的是一个为 avoid,一个为 insert 的情况,也就是说要避免一些词,但在真正插入一句的时候权限是否满足。

逻辑就是去遍历当前权限下不良单词集的所有单词,每一单词都到 b 要求插入的里面去找,只要有一个能找到,它的索引值大于等于零意味着不允许插入,如果整个循环完成后都没有被 return,说明所有的不良单词在当前要插入的这句中没有出现,因此返回 true。可以看到权限的逻辑是完全受控的,可以自己进行定义,包括它的action以及action于 action 之间配对出现的时候它们的隐含关系都可以被控制。

4.得到不良单词集

详细代码:

/**

Gets the bad words that this permission rule describes.

@return a set of the bad words

*/

public Set<String> badwordSet( )

{

set<String> set = new HashSet<String>();

set.addAl1(Arrays.asList(getName( ).split(",")));

return set;

}

private String action;

} 

把 getName 中要插入的部分用逗号隔开,就可以得到一个数据,这个数组里的所有东西就分至 set 中,这就是不良单词集的获取方式。这里需要注意的一点就是 Arrays 和 Set 的区别,Set 是剔除重复元素的,如果写入了sex、C++和drugs 后又写入了一遍 sex,显然不需要两个 set,就会把其中一个 sex 分至 set 进行剔除。用逗号隔开的逻辑很简单,因为在前面就是使用逗号来将不同的单词分隔开的,以上就是完整的自定义权限的例子。

 

三、运行效果

运行的前提是使用用安全管理器来完成,方便大家观看清晰,额外写入了一个辅助类,辅助类写的是刚才创建的WordCheckTextArea,并设计了相关尺寸,然后定义Jframe,同样也进行尺寸的设计,把前面的 words加 到J frame中,Jframe 相当于一个窗体,有了窗体才可以把内容写入。下面要求 append(sex)查看效果,policy文件里可以看到 grant permission,它对于 sex、drug、C++要 avoid,权限的代码和前面所细致讲解的内容一致。text area是每次需要 append 的文本的时候使用安全管理器查看系统里面是否存在完全管理器。Include 作为辅助类进行运行。

1.不用任何安全管理器的运行效果

创建完之后要去插入 sex 单词,可以看到 sex 是出现在 text area 单词里的。

2.使用安全管理器的运行效果

使用用安全管理器去运行代码后可以看到,运行结果里是不含任何内容的,还可以看到报出的异常信息:报出权限后就可以访问了。因为在使用安全管理器运行的时候,在本地就发现了 policy 文件并使用这 个 policy 运行,但是该policy里含有权限,所以在插入sex脚本的时候需要新创建插入 sex 的权限,安全管理器在检查的时会将其拒绝。上述所讲抛出的异常如下:

 image.png

在第一遍运行的时候没有写:Djava.security.manager wordsInput,是直接运行的,在 wordsInput可以看到能够显示出 sex。无论使用哪 种 policy 文件运行,在Java虚拟运行时,前提是要让其在有安全管理器执行的情况下完成,否则policy 不会起任何作用。即 policy 只有在安装安全管理器的系统中运行时才会起作用,因此在直接运行省掉中间参数时 sex 会显示出来。安全管理器和权限是两个密不可分的东西,本节的题目就是安全管理和权限,二者不可分割。需要注意的是何为 policy,通过 policy 的文件可以知道 policy 是把代码定义成域,每一个域都有 permission 的集合,代码可以有很多的域,而不同的域有不同的代码集合,解释起来非常简单,但是答案千奇百怪。实际上 policy 就是在定义域,代码已经不再区分远程和本地了,这个概念已经不存在了,代码分为不同的域,不同的域有不同的权限级,权限级受安全管理器的控制。使用安全管理器和权限级来进行自定义权限或者是系统定义权限的检查时级别更高一些,它是由权限的检查来完成的,由安全管理器通过权限 policy 实现。之前讲到的使用用户名和密码完成的,只能称为authentication 认证,而现在讲的 authorization 叫作授权,属于经常提到的3A 中的两 A,另外一A  是审计,表示未来能否对整个系统的用户登录和发布权限通过日志进行审计,合起来就是 tripleA(3A)。tripleA 的服务器是用于实践的,而目前讲到的 security manager 和 policy 属于授权的范围。

 

四、用户的认证

认证之前都是通过 password 和用户名字存储到数据库里面实现的,除了用户名和密码来进行认证之外,还有其他的方式实现用户的认证。实际上在 Java 之 前的旧版本里有一非常完整的体系——Java 授权与认证服务 JAAS。前面讲到的 policy 在定义 domen 有三种方式,分别是 codebase、principal 和 signalby。

1.使用 codebase 进行授权

前面所体现的是 codebase,表示代码的位置。刚才的例子里都只写了 grant,没有后面的具体位置,这是因为规定如果默认不在后面写出具体地址写,就是表示对当前 policy 所在的目录进行授权,即 class 文件和 policy放在了一起。

2.使用 principal 进行授权

第二类是 principal,principal 指无论代码从何而来,也无论代码是由谁开发的,只要当前代码在执行的时候,执行的线程具有 physical 特征,就允许认证。codebase 表示代码是从何而来,signalbybioassay 代码的开发者是谁,而principal 表示无论代码由谁写入,也无论代码从何而来,只需要明确代码的执行者的特征,要按照执行者来进行判断。principal 的意思是:首先给系统安装安全管理器,所有的事情都要从安全管理器开始。

具体代码:

try{

System.setSecurityManager(new SecurityManager());

LoginContext context = new LoginContext("Login1");

// defined in JAAS configuration file

context.login();

//get the authenticated Subject

Subject subject = context.getSubject();

...

context.logout();

}

catch (LoginException exception)

// thrown if login was not successful{

exception.printStackTrace();

}

既然是要通过代码的执行者进行认证,就需要一个对用户进行认证的设置,创建 LoginContext 作为用户在认证时的上下文对象。在一个系统里面类似于这种认证的配置有很多,需要选择出最合适的一个。Java 的认证与授权服务在系统里面含有 JASS.config 文件。

Jaas.config

Login1

{

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

com.whizzbang.auth.module.RetinaScanModule sufficient;

};

Login2{

};

文件里面会有很多的配置,所有的配置都具有一个名字,例如 Login1、Login2。内容使用大括号括起起来,表示括号中的是一种配置,在种认证配置里面用户要想通过认证,必须要经过 Unix 的用户名密码登录,并且要经过视网膜扫描,然后会有些属性来描述模块,即无论怎样都必须经过 Unix 的资格模块认证。而 sufficient 和数学中的充分条件类似,由于每个人的视网膜都不相同,因此只要通过了视网膜扫描就通过了认证。通过认证代表的含义稍后进行详细讲解。可以理解为飞机场的安检,安检就是 Jaas.config;Login1表示对航班的机组人员进行安检,对机组人员的安检比较简单,检测工作证以及其身上是否携带危险物品就可以了;Login2表示对乘客进行安检,对乘客的安检比较复杂,首先要检查乘客的票件,然后核对他们的 ID 信息,再检查他们身上有无危险品,经过三个不同的模块的认证才能通过检查。这个文件是纯文本文件,Java 体系先进的地方在于只要改变配置,机组人员的安检过程就发生了变化,但是代码不需要进行修改,完全通过配置就可以实现。如果模块是一样的,但是配置的属性不一样,比如检查工作证是必须的,而扫描危险品是可选的,可以将扫描危险品改成 optional 可选,表示检查与否都可以。通过他们的不同组合可以发现在代码不修改的情况下,可以比较灵活的实现认证过程的定制,这些模块可以随意组合,进行任意的对叠,这种体系可以叫做可堆栈 stackable,能够任意的堆放和丢弃。这种体系完全依靠配置来实现,非常的灵活。 

详细代码:

try{

System.setSecurityManager(new SecurityManager());

LoginContext context = new LoginContext("Login1");

// defined in JAAS configuration file

context.login();

// get the authenticated Subject

Subject subject = context.getSubject();

context.logout();

}

catch (LoginException exception)

// thrown if login was not successful

{

exception.printStackTrace();

}

所谓的 login1就是对当前执行的代码线程进行登录,使用 login1的配置进行认证。然后调用 context.login,context.login 实际上会经历一系列非常复杂的过程,过程如下图所示
image.png

创建了 loging context 对象后要传递的是 login1参数,在创建时会与服务器进行交流,读取 jass.config文件,然后在文件里面寻找 login1的配置。login1的配置有两个模块,会分别把模块加载出来,紧接着代码调用了 login 方法,背后发生的事情是依次调用 login 里所有模块的方法。每个 login 方法可能都需要输入一些内容,比如用户名密码或者身份证 ID 等信息。通过 Call back 拿到输入的信息,可以自定义一个 CallbackHandler,因为返回的只是 callback 对象,填入的可以是图形化界面,也可以是命令行,取决于传入时的选择,将 c allback 回来的内容填上。login 按照其处理逻辑,根据写入的 callback 来判断是否登录成功。如果登录成功就打出标记,这些信息汇总到一起之后再根据在  login1的设置,确定这些参数是必须的还是充分的,决定此次 login 是否成功。把上述内容对应到飞机安检的例子里,有助于大家的理解。到达飞机场之后要去登机,于是开始安检过程,安检过程需要在后台判断属于机组人员还是乘客。如果是乘客,会提示先去更换登机牌通过安检后走到飞机的登机口才能成功登机,即创建了一套流程里的模块。

开始安检时依次到三个模块进行操作,首先到办理登机牌的柜台办理登机牌,然后去安检处进行安检,最终在登机口进行检查。在办理登机牌的时候就会要求出示身份证或者其他有效证件,即c allback 进去内容。callback可以理解为在办理登机牌时提供的托牌,或者是办理登机牌的柜台,需要将证件放到 callback 里(放到台子上)以供检查。在放置的时候一种是自己直接将证件放入,还有一种在防疫阶段不想通过手进行直接的接触,可以拿镊子把证件放到台子上,到底是图形化界面还是命令行就对应着是用手还是用镊子,完全取决于自己,所以自己要有一个callbackhandler,即放置所需内容的方式由自己确定。放置完成之后,对方根据自己的逻辑,通过数据库进行核对你是否购买了本次航班的机票来决定是否办理登记牌。安检处在登机牌上盖章,在登机口进行勾画,最终可以坐到飞机上。

之所以可以坐在飞机上,是因为通过了三个模块的检查。坐上飞机之前的你和坐上飞机之后的你有所不同,坐上飞机的你是经过认证之后的你,具有了经历过安检、盖章以及登机口上画戳的票,对应的在 Java 体系里面叫作principle,可以把它翻译成特征。这些特征在存储在  login 之后,在 contact 上 get subject,principal 就在subject中。所以当底下 用 subject 去执行动作的时候,安全管理器就可以去检查该 subject 里面是否含有 principal,如果有就可以执行操作,如果没有就不可以。执行完动作之后一定要 logout 相当于下了飞机,类似的特征就没有用处了,会把 subject 里面所有东西消毁掉。

获取完 subject 之后操作的是这样的:

使用 subject 类的 doAsPrivilege 方法,用登录过的 subject 主体去执行动作,动作为 privilegedAction特权动作,有权限的人才能够执行这个动作。具体的执行方法是:在 run 方 法里随便写入需要完成的任务,意思就是这一段逻辑不是任何人都可以执行的,必须要经过刚才的认证并且 subject 里带的 principal 还必须是 policy 文件里面允许的内容,具体代码如下:

An Example

- AuthTest.javaimport 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 SecurityManager());

LoginContext context = new LoginContext("Login1");

context.login();

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

开始创建 SecurityManager,然后用 login1来创建 LoginContext 来调用 Login,Login1的各模块要求登录,检查要求传入 callback 的代码,根据c allback 内容检查是否通过,登录完成之后获取 subject。使用 subject 去执行特权动作,特权动作就是读取 user.home 的属性,在操作的时候安全管理器会检查 subject 到底有无属性。自定义的特权属性可以实现 PrivilegeAction 类,接口的 run 是用来读取系统属性的,在执行动作的时候是以 subject 完成的,而这个subject是经过认证过之后得到的,到底有没有权限取决于后面 grant 是如何设定 policy 文件的。policy 文件用以授权,代码要想创建是需要权限的,因此需要先创立权限,doAsPrivilege 也是需要权限的。把这两个权限授权给代码,所有的代码无论谁去执行,只要含有 principal 就有权限读取系统属性。刚才要求按照 loin1进行登录,Login1的配置是:必须要Unix模块进行登录,一旦登录通过 UnixLogin 会要求输入用户名和密码,拿到用户名和密码之后就会赋予这样的权限,检查后可以发现该用户具有这样的权限。由于例子不易进行演示,所以自定义了 Login module,然后运行即可。

 

五、书写 Login Module

详细代码:

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

grant principal SimplePrincipal "role=admin" {…}

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

Harry|secret|admin

Carl|guessme|HR

. 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.getPrincipals();

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

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

原来使用 Unix 的用户名和密码进行登录,现在进行定义。把他们的用户名、密码和角色专门写一个文本文件,然后再定义一个 simple principal,将来会去写它授权文件名。如果经过登录之后的用户具有 simple principal的principal,并且它的 principal 的名字叫做 role=admin,它就具备了下面的权限。

现在要写的 module 具有以下功能:凡是输入的用户名和密码与上面的对应,就会给在 subject 里添加两个SimplePrincipal,分别是用户名=Harry 和 role=admin。这样就可以和刚才的 policy 配合起来授权,此时再运行的时候就不需要 UnixLoginModule,而是使用自定义的 LoginModule 来解决问题。

由于时间关系,现只给大家展示代码运行后的效果,把刚才所讲的代码全部输入,包括密码、角色、执行特权动作需要的权限,比如创建 LoginContext,doAsPriviledge 等权限。

只要有了 S implePrincipal,就能读取所有系统属性。还有自定义的 LoginModule。

运行 LoginContext,把用户名和密码放到数字机中,使用用户名密码进行登录,操作时不可以直接点击,因为代码里安装了 securityManager,但是没有指定 policy 和 config,所以需要逐行输入。大家亲自运行后就可以理解通过的意思。

相关文章
|
6月前
|
安全 Java 数据安全/隐私保护
Security快速入门
Security快速入门
|
存储 安全 Java
Web Security 之 Insecure deserialization
Web Security 之 Insecure deserialization
53 0
|
自然语言处理 算法 安全
Security2.2|学习笔记
快速学习 Security2.2
123 0
Security2.2|学习笔记
|
缓存 安全 Java
Security2 1|学习笔记
快速学习 Security2 1
232 0
Security2 1|学习笔记
|
存储 算法 安全
Security2 3|学习笔记
快速学习 Security2 3
Security2 3|学习笔记
|
安全 Java 数据安全/隐私保护
案例之认证服务security配置|学习笔记
快速学习案例之认证服务security配置
案例之认证服务security配置|学习笔记
|
存储 前端开发 安全
Security RememberMe原理分析
Security RememberMe原理分析
177 0
|
安全 NoSQL Java
security和oauth2.0的整合
security和oauth2.0的整合 之前已经介绍过security的相关的介绍,现在所需要做的就是security和oauth2.0的整合,在原有的基础上我们加上一些相关的代码;代码实现如下: pom.
1545 0
|
安全 网络安全 数据安全/隐私保护