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. Bootstrap-CL:页面标题

    ylbtech-Bootstrap-CL:页面标题 1.返回顶部 1. Bootstrap 页面标题(Page Header) 页面标题(Page Header)是个不错的功能,它会在网页标题四周添加 ...

  2. FilenameFilter 文件名过滤

    public static final FilenameFilter JSON_CONFIG_FILE_FILTER = new FilenameFilter() {         @Overrid ...

  3. SpringBoot入门篇--对于JSON数据的返回以及处理一

    在后台的开发过程中不可避免的就是一系列对JSON数据的返回,需要我们进行的就是提供各种各样的数据.一般情况下数据类型最常用的就是JSON以及XML,在这里我们就讲讲在SpringBoot里面我们怎样进 ...

  4. ggplot2 texts : Add text annotations to a graph in R software

    http://www.sthda.com/english/wiki/ggplot2-texts-add-text-annotations-to-a-graph-in-r-software Instal ...

  5. Xshell连接虚拟机突然被拒,提示再次输入密码。。。

    sudo vim /etc/ssh/sshd_config 将 # Authentication: LoginGraceTime 120 PermitRootLogin prohibit-passwo ...

  6. tensorflow-base_operations

    # -*- coding: utf-8 -*-import tensorflow as tf# 基本的常量操作,通过构造函数返回值 定义值的操作operationsa = tf.constant(2) ...

  7. Spring配置项<context:annotation-config/>解释说明

    转自:https://blog.csdn.net/techbirds_bao/article/details/9241371 在基于主机方式配置Spring的配置文件中,你可能会见到<conte ...

  8. Richview 首页 奇偶页 不同页眉页脚

    首页 奇偶页 不同页眉页脚 ScaleRichView v6.0 Different headers and footers for the first page, for odd and even ...

  9. sql日期查询

    select getdate() ,getdate()) ,getdate()) ) ,getdate())) ,getdate())) Select datename(weekday, getdat ...

  10. 初认识ZK

    转自:https://www.jianshu.com/p/8e322462bcca 前言: 前段时间做了sdk直播服务,由于给游戏接入,所以必须要考虑并发性能问题,大家知道直播聊天,房间人数多了的话是 ...