加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java Web系列:JAAS认证和授权基础

发布时间:2020-12-14 06:26:38 所属栏目:Java 来源:网络整理
导读:1.认证和授权概述 (1)认证:对用户的身份进行验证。 .NET基于的RBS(参考1)的认证和授权相关的核心是2个接口System.Security.Principal. 和System.Security.Principal. 。我们自己实现认证过程,通过 来设置和读取认证结果。认证成功后设置认证状态和标识

1.认证和授权概述

(1)认证:对用户的身份进行验证。

.NET基于的RBS(参考1)的认证和授权相关的核心是2个接口System.Security.Principal.和System.Security.Principal.。我们自己实现认证过程,通过来设置和读取认证结果。认证成功后设置认证状态和标识。

Java内置了的(参考2),核心是javax.security.auth.类和javax.security.接口。java对认证过程也提供了2个类型,javax.security.auth.login.类和javax.security.auth.spi.接口。我们自己实现认证过程,但只要实现了LoginModule就可以通过LoginContext使用一致的语法。

(2)授权:对用户的权限进行验证,通常使用(角色)管理权限。

.NET的支持基于角色的授权。.NET的IPrincipal接口的方法是授权的核心。有两种方式使用:1.使用内置的IPrincipal对象(如),在认证的同时加载用户的角色roles。2.自定义IPrincipal实现,实现自己的IsInRole逻辑。ASP.NET中实现的的就通过将逻辑转发给System.Web.Security.静态类。Roles依赖System.Web.Security.接口实现角色的查询,我们可以通过web.config的相关节点来配置自定义的RoleProvider。

JAAS的IPrincipal接口没有提供IsInRole方法,我们有2个选择,要么通过多个IPrincipal表示角色,要么自定义实现IPrincipal添加角色支持。容器实现的org.apache.catalina.realm.就和.NET中的System.Security.Principal.十分类似的角色实现。

Java的Subject类和IPrincipal接口与.NET的IPrincipal接口和IIdentity的接口不容易对应。为了便于统一理解.NET和Java的核心类型,我们可以从成员的理解,可以认为Java的Principal类型相当于.NET中的IPrincipa和IIdentity两个类型的作用。Subject只是作为Principal的聚合根。之前提到的Tomcat容器中的GenericPrincipal就即提供了hasRole和getName成员。Tomcat实现的HttpServletRequest对应的成员就是通过GenericPrincipal实现的。

2..NET Web认证和授权

ASP.NET Forms认证主采用RolePrincipal主体,未认证用户设置为GenericIdentity标识,已认证用户设置为FormsIdentity。RolePrincipal在验证角色时将使用Roles静态类通过RoleProvider进行角色验证。通过HttpContext.User可以直接调用主体。

实现一个最简单的自定义RoleProvider只需要继承并实现GetRolesForUser和IsUserInRole两个方法,通常可以使用委托在Application_Start中注入的方式实现通用的RoleProvider。

ASP.NET的forms验证通过FormsAuthentication发送和注销用于认证的token,通过配置web.config可以让不同的Web服务器以相同的方式对token加密和解密以适应Web服务器负载均衡。不适用cookie承载token时,可以自定义认证逻辑,比如通过url参数方式承载token配合ssl用于app客户端验证等。

.NET的认证和授权示意图:

自定义RoleProvider的示例,省略了不需要实现的部分代码,GetRolesForUserDelegate和IsUserInRoleDelegate在Application_Start中注入即可彻底实现RoleProvider和应用服务代码的解耦:

public class SimpleRoleProvider : RoleProvider { public static Func GetRolesForUserDelegate;
    public static Func<string,string,bool><span> IsUserInRoleDelegate;

    public override string[] GetRolesForUser(string<span> username)
    {
        return<span> GetRolesForUserDelegate(username);
    }

    public override bool IsUserInRole(string username,string<span> roleName)
    {
        return<span> IsUserInRoleDelegate(username,roleName);
    }
}</span></span></span></span></span></span></span></pre>

Forms身份验证和RoleProvider的分别定义在web.config配置文件中。ASP.NET的配置文件示例(省略了其他配置):

.NET中还有用于配置Pricipal的两个方法System.AppDomain.和System.AppDomain.以及控制访问的两个类型System.Security.Permissions.和System.Security.Permissions.

3.JAAS

接口定义了6个验证和授权相关的方法()、()、()、()、()、()。类似ASP.NET,Forms身份验证也在配置文件中进行配置。但由于Java热衷于定义一堆接口将实现推迟到容器级别,依赖的具体的的配置也必须在容器中进行配置。因此除了web.xml,还需要配置在容器中配置JAAS的配置文件。JAAS的示意图:

(1)JAAS 内置的登录模块使用:NTLoginModule:

配置:

代码

<span style="color: #0000ff;">import<span style="color: #000000;"> java.security.Principal;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.login.LoginContext;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.login.LoginException;

<span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> App {
<span style="color: #0000ff;">public <span style="color: #0000ff;">static <span style="color: #0000ff;">void<span style="color: #000000;"> main(String[] args) {
System.setProperty("java.security.auth.login.config"<span style="color: #000000;">,Thread.currentThread().getContextClassLoader().getResource("jaas.config"<span style="color: #000000;">).getPath());
<span style="color: #0000ff;">try<span style="color: #000000;"> {
LoginContext lc = <span style="color: #0000ff;">new LoginContext("first"<span style="color: #000000;">);
lc.login();
System.out.println(lc.getSubject().getPrincipals().size());
<span style="color: #0000ff;">for<span style="color: #000000;"> (Principal item : lc.getSubject().getPrincipals()) {
System.out.println(String.format("%s principal:%s"<span style="color: #000000;">,item.getClass().getTypeName(),item.getName()));
}
lc.logout();
System.out.println(lc.getSubject().getPrincipals().size());
} <span style="color: #0000ff;">catch<span style="color: #000000;"> (LoginException e) {
e.printStackTrace();
}
}
}

?(2)JAAS Tomcat容器下的登录模块使用(参考3):

用于配置Forms认证:web.xml

Archetype Created Web Application Admin /admin/* admin
<span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;security-role</span><span style="color: #0000ff;"&gt;></span>
    <span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;role-name</span><span style="color: #0000ff;"&gt;></span>admin<span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;role-name</span><span style="color: #0000ff;"&gt;></span>
<span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;security-role</span><span style="color: #0000ff;"&gt;></span>
<span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;login-config</span><span style="color: #0000ff;"&gt;></span>
    <span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;auth-method</span><span style="color: #0000ff;"&gt;></span>FORM<span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;auth-method</span><span style="color: #0000ff;"&gt;></span>
    <span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;form-login-config</span><span style="color: #0000ff;"&gt;></span>
        <span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;form-login-page</span><span style="color: #0000ff;"&gt;></span>/login.html<span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;form-login-page</span><span style="color: #0000ff;"&gt;></span>
        <span style="color: #0000ff;"&gt;<</span><span style="color: #800000;"&gt;form-error-page</span><span style="color: #0000ff;"&gt;></span>/error.html<span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;form-error-page</span><span style="color: #0000ff;"&gt;></span>
    <span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;form-login-config</span><span style="color: #0000ff;"&gt;></span>
<span style="color: #0000ff;"&gt;</</span><span style="color: #800000;"&gt;login-config</span><span style="color: #0000ff;"&gt;></span>

<span style="color: #0000ff;"></<span style="color: #800000;">web-app<span style="color: #0000ff;">>

用于JAAS的LoginModule的配置:/main/java/resources/jaas.config

用于Tomcat的配置:/main/webapp/META-INF/context.xml配置(这个依赖至少还在项目内,不需要修改tomcat):

用于Tomcat的UserClass和RoleClass代码:

<span style="color: #0000ff;">public <span style="color: #0000ff;">class UserPrincipal <span style="color: #0000ff;">implements<span style="color: #000000;"> Principal {

</span><span style="color: #0000ff;"&gt;private</span><span style="color: #000000;"&gt; String _name;

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; UserPrincipal(String name) {
    </span><span style="color: #0000ff;"&gt;this</span>._name =<span style="color: #000000;"&gt; name;
}

@Override
</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; String getName() {

    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;._name;
}

}

RoleClass:

<span style="color: #0000ff;">public <span style="color: #0000ff;">class RolePrincipal <span style="color: #0000ff;">implements<span style="color: #000000;"> Principal {

</span><span style="color: #0000ff;"&gt;private</span><span style="color: #000000;"&gt; String _name;

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; RolePrincipal(String name) {
    </span><span style="color: #0000ff;"&gt;this</span>._name =<span style="color: #000000;"&gt; name;
}

@Override
</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; String getName() {

    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;._name;
}

}

用于JAAS配置文件初始化的代码(为了依赖tomcat的配置,在Filter中设置配置文件):

<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.Filter;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.FilterChain;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.FilterConfig;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.ServletException;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.ServletRequest;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.ServletResponse;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.annotation.WebFilter;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.servlet.http.HttpServletRequest;

@WebFilter("/*"<span style="color: #000000;">)
<span style="color: #0000ff;">public <span style="color: #0000ff;">class MyFilter <span style="color: #0000ff;">implements<span style="color: #000000;"> Filter {

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> init(FilterConfig filterConfig) <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; ServletException {
    </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; TODO 自动生成的方法存根</span>
    System.setProperty("java.security.auth.login.config"<span style="color: #000000;"&gt;,Thread.currentThread().getContextClassLoader().getResource(</span>"jaas.config"<span style="color: #000000;"&gt;).getPath());
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
        </span><span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; IOException,ServletException {
    chain.doFilter(</span><span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; MyRequest((HttpServletRequest) request),response);

}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; destroy() {
    </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; TODO 自动生成的方法存根</span>

<span style="color: #000000;">
}

}

用于登录的login.html:

Insert title here <<UserName<< <<Password<< <<<

MyLoginModule实现:其中的三个name属性必须是固定值:

<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.Subject;
<span style="color: #0000ff;">import
<span style="color: #000000;"> javax.security.auth.callback.Callback;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.callback.CallbackHandler;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.callback.NameCallback;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.callback.PasswordCallback;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.callback.UnsupportedCallbackException;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.login.LoginException;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.security.auth.spi.LoginModule;

<span style="color: #0000ff;">public <span style="color: #0000ff;">class MyLoginModule <span style="color: #0000ff;">implements<span style="color: #000000;"> LoginModule {

</span><span style="color: #0000ff;"&gt;private</span><span style="color: #000000;"&gt; CallbackHandler handler;
</span><span style="color: #0000ff;"&gt;private</span><span style="color: #000000;"&gt; Subject subject;

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> initialize(Subject subject,CallbackHandler callbackHandler,Map<String,?><span style="color: #000000;"&gt; sharedState,Map</span><String,?><span style="color: #000000;"&gt; options) {
    handler </span>=<span style="color: #000000;"&gt; callbackHandler;
    </span><span style="color: #0000ff;"&gt;this</span>.subject =<span style="color: #000000;"&gt; subject;
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;boolean</span> login() <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; LoginException {

    Callback[] callbacks </span>= <span style="color: #0000ff;"&gt;new</span> Callback[2<span style="color: #000000;"&gt;];
    callbacks[</span>0] = <span style="color: #0000ff;"&gt;new</span> NameCallback("login"<span style="color: #000000;"&gt;);
    callbacks[</span>1] = <span style="color: #0000ff;"&gt;new</span> PasswordCallback("password",<span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;);

    </span><span style="color: #0000ff;"&gt;try</span><span style="color: #000000;"&gt; {
        handler.handle(callbacks);
        String name </span>= ((NameCallback) callbacks[0<span style="color: #000000;"&gt;]).getName();
        String password </span>= String.valueOf(((PasswordCallback) callbacks[1<span style="color: #000000;"&gt;]).getPassword());

        </span><span style="color: #0000ff;"&gt;if</span> (name != <span style="color: #0000ff;"&gt;null</span> &amp;&amp; name.equals("user123") &amp;&amp; password != <span style="color: #0000ff;"&gt;null</span> &amp;&amp; password.equals("pass123"<span style="color: #000000;"&gt;)) {
            </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;;
        }

        </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; If credentials are NOT OK we throw a LoginException</span>
        <span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span> LoginException("Authentication failed"<span style="color: #000000;"&gt;);

    } </span><span style="color: #0000ff;"&gt;catch</span><span style="color: #000000;"&gt; (IOException e) {
        </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LoginException(e.getMessage());
    } </span><span style="color: #0000ff;"&gt;catch</span><span style="color: #000000;"&gt; (UnsupportedCallbackException e) {
        </span><span style="color: #0000ff;"&gt;throw</span> <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LoginException(e.getMessage());
    }
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;boolean</span> commit() <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; LoginException {
    subject.getPrincipals().add(</span><span style="color: #0000ff;"&gt;new</span> UserPrincipal("user123"<span style="color: #000000;"&gt;));
    subject.getPrincipals().add(</span><span style="color: #0000ff;"&gt;new</span> RolePrincipal("admin"<span style="color: #000000;"&gt;));
    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;;
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;boolean</span> abort() <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; LoginException {
    </span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; TODO 自动生成的方法存根</span>
    <span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;false</span><span style="color: #000000;"&gt;;
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;boolean</span> logout() <span style="color: #0000ff;"&gt;throws</span><span style="color: #000000;"&gt; LoginException {
    subject.getPrincipals().clear();
    </span><span style="color: #0000ff;"&gt;return</span> <span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;;
}

}

在.NET的RBS基础上实现RBAC(参考4)是可行的,但JAAS....。JAAS只要用过的人都对其印象

4.参考

(1)https://msdn.microsoft.com/en-us/library/52kd59t0(v=vs.90).aspx

(2)http://docs.oracle.com/javase/8/docs/technotes/guides/security/jaas/JAASRefGuide.html

(3)http://www.byteslounge.com/tutorials/jaas-form-based-authentication-in-tomcat-example

(4)http://csrc.nist.gov/groups/SNS/rbac/

5.小结:

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!