1.认证和授权概述

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

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

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

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

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

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

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

# 成员 .NET Java
认证类型 AuthenticationType System.Security.Principal.IIdentity.AuthenticationType javax.servlet.http.HttpServletRequest.getAuthType()
标识名称 Name System.Security.Principal.IIdentity.Name java.security.Principal.getName()
角色验证 IsInRole System.Security.Principal.IPrincipal.IsInRole javax.servlet.http.HttpServletRequest.isUserInRole()

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<string, string[]> GetRolesForUserDelegate; public static Func<string, string, bool> IsUserInRoleDelegate; public override string[] GetRolesForUser(string username)
{
return GetRolesForUserDelegate(username);
} public override bool IsUserInRole(string username, string roleName)
{
return IsUserInRoleDelegate(username, roleName);
}
}

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

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<authentication mode="Forms">
<forms loginUrl="~/Home/Login" cookieless="UseCookies" slidingExpiration="true" />
</authentication>
<roleManager defaultProvider="SimpleRoleProvider" enabled="true">
<providers>
<clear />
<add name="SimpleRoleProvider" type="Onion.Web.SimpleRoleProvider" />
</providers>
</roleManager>
</system.web>
</configuration>

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

3.JAAS

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

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

配置:

first{
com.sun.security.auth.module.NTLoginModule Required debug=true;
};

代码

package com.test.jaas1;

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

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

用于配置Forms认证:web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Archetype Created Web Application</display-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin</web-resource-name>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint> <security-role>
<role-name>admin</role-name>
</security-role>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/error.html</form-error-page>
</form-login-config>
</login-config>
</web-app>

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

MyLogin{
MyLoginModule Required debug=true;
};

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

<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Realm className="org.apache.catalina.realm.JAASRealm"
appName="MyLogin"
userClassNames="UserPrincipal"
roleClassNames="RolePrincipal" />
</Context>

用于Tomcat的UserClass和RoleClass代码:

import java.security.Principal;

public class UserPrincipal implements Principal {

    private String _name;

    public UserPrincipal(String name) {
this._name = name;
} @Override
public String getName() { return this._name;
} }

RoleClass:

import java.security.Principal;

public class RolePrincipal implements Principal {

    private String _name;

    public RolePrincipal(String name) {
this._name = name;
} @Override
public String getName() { return this._name;
} }

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

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest; @WebFilter("/*")
public class MyFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO 自动生成的方法存根
System.setProperty("java.security.auth.login.config",
Thread.currentThread().getContextClassLoader().getResource("jaas.config").getPath());
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(new MyRequest((HttpServletRequest) request), response); } @Override
public void destroy() {
// TODO 自动生成的方法存根 } }

用于登录的login.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="post" action="j_security_check">
<table>
<tr><td><label>UserName</label></td><td><input type="text" name="j_username"></td></tr>
<tr><td><label>Password</label></td><td><input type="password" name="j_password"></td></tr>
<tr><td></td><td><input type="submit" value="Login"></td></tr>
</table>
</form>
</body>
</html>

MyLoginModule实现:其中的三个name属性必须是固定值:j_security_check、j_username和j_password。

import java.io.IOException;
import java.util.Map; import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule; public class MyLoginModule implements LoginModule { private CallbackHandler handler;
private Subject subject; @Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options) {
handler = callbackHandler;
this.subject = subject;
} @Override
public boolean login() throws LoginException { Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("login");
callbacks[1] = new PasswordCallback("password", true); try {
handler.handle(callbacks);
String name = ((NameCallback) callbacks[0]).getName();
String password = String.valueOf(((PasswordCallback) callbacks[1]).getPassword()); if (name != null && name.equals("user123") && password != null && password.equals("pass123")) {
return true;
} // If credentials are NOT OK we throw a LoginException
throw new LoginException("Authentication failed"); } catch (IOException e) {
throw new LoginException(e.getMessage());
} catch (UnsupportedCallbackException e) {
throw new LoginException(e.getMessage());
}
} @Override
public boolean commit() throws LoginException {
subject.getPrincipals().add(new UserPrincipal("user123"));
subject.getPrincipals().add(new RolePrincipal("admin"));
return true;
} @Override
public boolean abort() throws LoginException {
// TODO 自动生成的方法存根
return false;
} @Override
public boolean logout() throws LoginException {
subject.getPrincipals().clear();
return true;
} }

在.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.小结:

JAAS抽象的不切实际,实现又全靠容器,不同容器的实现还不一致,IPrincipal又不能直接支持Servlet认证和授权相关的方法。至少应该像.NET一样提供数据结构级别的角色认证类型,而不是跑偏成现在这样。容器要么自己扩展IPrincipal支持角色,要么通过配置传入指定类型的IPrincipal子类来区分角色和用户。只能希望Apache Shiro和Spring等第三方提供的一些实现能有更高的可用性。

Java Web系列:JAAS认证和授权基础的更多相关文章

  1. Java Web系列:Spring依赖注入基础

    一.Spring简介 1.Spring简化Java开发 Spring Framework是一个应用框架,框架一般是半成品,我们在框架的基础上可以不用每个项目自己实现架构.基础设施和常用功能性组件,而是 ...

  2. Shiro集成web环境[Springboot]-认证与授权

    Shiro集成web环境[Springboot]--认证与授权 在登录页面提交登陆数据后,发起请求也被ShiroFilter拦截,状态码为302 <form action="${pag ...

  3. Web Api 2 认证与授权 2

    HTTP Message Handler 在 Web Api 2 认证与授权 中讲解了几种实现机制,本篇就详细讲解 Message Handler 的实现方式 关于 Message Handler 在 ...

  4. Java Web系列:Spring Security 基础

    Spring Security虽然比JAAS进步很大,但还是先天不足,达不到ASP.NET中的认证和授权的方便快捷.这里演示登录.注销.记住我的常规功能,认证上自定义提供程序避免对数据库的依赖,授权上 ...

  5. Java Web系列:Spring MVC基础

    1.Web MVC基础 MVC的本质是表现层模式,我们以视图模型为中心,将视图和控制器分离出来.就如同分层模式一样,我们以业务逻辑为中心,把表现层和数据访问层代码分离出来是一样的方法.框架只能在技术层 ...

  6. Java Web系列:Java Web 项目基础

    1.Java Web 模块结构 JSP文件和AXPX文件类似,路径和URL一一对应,都会被动态编译为单独class.Java Web和ASP.NET的核心是分别是Servlet和IHttpHandle ...

  7. Java Web开发之Servlet、JSP基础

    有好多年不搞Java Web开发了,这几天正好国庆放假,放松之余也有兴趣回头看看Java Web开发技术的基础. 我们都知道,Servlet是Java Web开发的重要基础,但是由于Servlet开发 ...

  8. 关于 Web Api 2 认证与授权

    认证与授权 认证与授权,Authentication and Authorize,这个是两个不同的事.认证是对访问身份进行确认,如验证用户名和密码,而授权是在认证之后,判断是否具有权限进行某操作,如 ...

  9. Java Web系列:Spring Boot 基础

    Spring Boot 项目(参考1) 提供了一个类似ASP.NET MVC的默认模板一样的标准样板,直接集成了一系列的组件并使用了默认的配置.使用Spring Boot 不会降低学习成本,甚至增加了 ...

随机推荐

  1. Windows 10 修改系统环境变量后,CMD生效,Powershell未生效

    Windows 10 修改系统环境变量后 1.CMD重新打开,新环境变量已经生效: 2.Shift+右键Powershell重新打开,新环境变量没有生效: 3.貌似服务(Apache24)中使用的环境 ...

  2. [转]MVC 分页

    本内容代码段抄自传智视频 /// <summary> /// 数据库分页 /// </summary> static List<dynamic> GetPageLi ...

  3. 在eclipse中使用mybatis-generator自动创建代码

    1.eclipse中安装插件,地址:http://mybatis.googlecode.com/svn/sub-projects/generator/trunk/eclipse/UpdateSite/ ...

  4. html页面转换成pdf

    一般页面都是.jsp页面,所以要把.jsp转换成html,在生成pdf,在网上找了好多方法,只有用一个插件,wkhtmltopdf-0.8.3.exe,生成的pdf会相对的好看. 先附上我做的.jsp ...

  5. 替换res\drawable中的图片

    现象 在android开发中,经常会需要替换res\drawable中的图片,打开res\layout下的文件预览布局页面发现图片已经被替换,但在模拟器或者真实机器上运行时发现该图片并没有被替换,还是 ...

  6. 常见报表的JS代码

    1.合并单元格 这个表格在报表里面算是比较典型的 1.里面的表格的列标题现在是写死的,其实这些可以通过配置进行 2.至于如果表头要进行合并(这种需求比较少,也比较容易实现) 3.至于统计:最好在后台按 ...

  7. 读Understanding the Linux Kernel, 3rd Edition有感

    14.3.2.2. Avoiding request queue congestion Each request queue has a maximum number of allowed pendi ...

  8. sqoop导出到mysql中文乱码问题总结、utf8、gbk

    sqoop导出到mysql中文乱码问题总结.utf8.gbk 今天使用sqoop1.4.5版本的(hadoop使用cdh5.4)因为乱码问题很是头痛半天.下面进行一一总结 命令: [root@sdzn ...

  9. 版本控制器svn详细

    2 svn介绍 2.1 项目管理中的版本控制问题 通常软件开发由多人协作开发,如果对代码文件.配置文件.文档等没有进行版本控制,将会出现很多问题: 备份多个版本,占用磁盘空间大 解决代码冲突困难 容易 ...

  10. Selenium Webdriver——Table类封装

    WebTable.java import java.util.List; import org.openqa.selenium.By; import org.openqa.selenium.WebEl ...