Matplotlib中的axis.Tick.set_zorder()函数:控制刻度线层级的利器
参考:Matplotlib.axis.Tick.set_zorder() function in Python
Matplotlib是Python中最流行的数据可视化库之一,它提供了丰富的绘图功能和自定义选项。在创建复杂的图表时,控制各个元素的绘制顺序变得尤为重要。本文将深入探讨Matplotlib中的axis.Tick.set_zorder()函数,这是一个强大的工具,用于调整刻度线的绘制层级,从而实现更精确的图表控制。
1. 理解zorder概念
在Matplotlib中,zorder是一个用于控制绘图元素堆叠顺序的参数。具有较高zorder值的元素会被绘制在具有较低zorder值的元素之上。默认情况下,不同类型的图表元素有不同的zorder值:
刻度线(Tick)作为轴的一部分,其默认zorder值通常与轴相同。通过使用set_zorder()方法,我们可以自定义刻度线的绘制顺序,使其在需要时位于其他元素之上或之下。
让我们看一个简单的例子,展示如何使用set_zorder()来调整刻度线的位置:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 6))
ax.set_title("How2matplotlib.com - Adjusting Tick zorder")
# 绘制一个简单的线条
ax.plot([0, 1, 2, 3, 4], [0, 2, 1, 3, 2], linewidth=5, color='lightblue', zorder=1)
# 获取x轴的刻度对象
xticks = ax.xaxis.get_major_ticks()
# 设置刻度线的zorder为高于线条
for tick in xticks:
tick.set_zorder(2)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
plt.show()
Output:
在这个例子中,我们首先绘制了一条粗线,然后将x轴刻度的zorder设置为2,使其显示在线条之上。这样,即使线条穿过刻度线的位置,刻度线也会清晰可见。
2. set_zorder()方法详解
set_zorder()方法是matplotlib.axis.Tick类的一个方法。它接受一个数值参数,用于设置刻度线的zorder值。语法如下:
tick.set_zorder(level)
其中,level是一个整数,表示要设置的zorder值。值越大,元素就会被绘制在越上层。
让我们通过一个更复杂的例子来深入理解set_zorder()的使用:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Complex zorder Example")
# 生成数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 绘制两条线
ax.plot(x, y1, color='red', linewidth=2, label='Sin', zorder=1)
ax.plot(x, y2, color='blue', linewidth=2, label='Cos', zorder=2)
# 添加一个矩形patch
rect = plt.Rectangle((4, -0.5), 2, 1, facecolor='yellow', alpha=0.5, zorder=1.5)
ax.add_patch(rect)
# 设置x轴刻度的zorder
for tick in ax.xaxis.get_major_ticks():
tick.set_zorder(3)
# 设置y轴刻度的zorder
for tick in ax.yaxis.get_major_ticks():
tick.set_zorder(3)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.legend()
plt.show()
Output:
在这个例子中,我们绘制了正弦和余弦曲线,并添加了一个矩形。通过设置不同的zorder值,我们控制了各个元素的绘制顺序。最后,我们将x轴和y轴的刻度线zorder设置为3,使其显示在所有其他元素之上。
3. 为什么要使用set_zorder()?
使用set_zorder()方法有几个重要的原因:
提高可读性:通过调整刻度线的zorder,可以确保它们不会被其他图表元素遮挡,从而提高图表的可读性。
美化图表:适当调整zorder可以创建出层次感更强、更美观的图表。
解决重叠问题:在复杂的图表中,元素之间可能会发生重叠。使用set_zorder()可以精确控制哪些元素应该在上层显示。
突出重要信息:通过将重要元素的zorder设置得更高,可以让它们更加突出。
让我们看一个例子,展示如何使用set_zorder()来解决重叠问题:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Solving Overlap with set_zorder()")
# 生成数据
x = np.linspace(0, 10, 100)
y = np.sin(x) * np.exp(-0.1 * x)
# 绘制主曲线
ax.plot(x, y, 'b-', linewidth=2, label='Main curve')
# 添加一些垂直线来标记特殊点
for i in range(1, 10, 2):
ax.axvline(x=i, color='r', linestyle='--', alpha=0.5, zorder=1)
# 设置刻度线的zorder
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(3)
# 添加一些文本注释
ax.text(5, 0.5, "Important\nregion", ha='center', va='center',
bbox=dict(facecolor='yellow', alpha=0.5, zorder=2))
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.legend()
plt.show()
Output:
在这个例子中,我们绘制了一条主曲线,添加了一些垂直线来标记特殊点,并加入了文本注释。通过设置刻度线的zorder为3,我们确保它们始终显示在其他元素之上,即使在有垂直线和文本框的区域也是如此。
4. set_zorder()与其他绘图元素的交互
set_zorder()不仅可以用于刻度线,还可以与其他Matplotlib绘图元素结合使用,以创建更复杂和精细的图表。让我们探讨一下set_zorder()如何与其他常见的绘图元素交互。
4.1 与线条(Lines)的交互
线条是Matplotlib中最基本的绘图元素之一。通过调整线条和刻度线的zorder,我们可以创建出各种有趣的效果。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Lines and Ticks zorder Interaction")
# 生成数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 绘制线条
line1 = ax.plot(x, y1, 'r-', linewidth=3, label='Sin', zorder=1)[0]
line2 = ax.plot(x, y2, 'b-', linewidth=3, label='Cos', zorder=2)[0]
# 设置刻度线的zorder
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(1.5)
# 添加图例
ax.legend()
# 添加交互功能
def on_click(event):
if line1.get_zorder() == 1:
line1.set_zorder(3)
line2.set_zorder(1)
else:
line1.set_zorder(1)
line2.set_zorder(2)
fig.canvas.draw()
fig.canvas.mpl_connect('button_press_event', on_click)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
plt.show()
Output:
在这个例子中,我们创建了两条线(正弦和余弦),并设置了不同的zorder。刻度线的zorder被设置为1.5,介于两条线之间。我们还添加了一个交互功能,点击图表可以切换两条线的zorder。
4.2 与填充区域(Fill_between)的交互
fill_between()函数用于填充两条线之间的区域。结合set_zorder(),我们可以创建出层次分明的填充图表。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Fill_between and Ticks zorder Interaction")
# 生成数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 填充区域
ax.fill_between(x, y1, y2, where=(y1 > y2), facecolor='green', alpha=0.3, zorder=1, label='y1 > y2')
ax.fill_between(x, y1, y2, where=(y1 <= y2), facecolor='red', alpha=0.3, zorder=2, label='y1 <= y2')
# 绘制线条
ax.plot(x, y1, 'b-', label='Sin', zorder=3)
ax.plot(x, y2, 'r-', label='Cos', zorder=3)
# 设置刻度线的zorder
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(4)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.legend()
plt.show()
Output:
在这个例子中,我们使用fill_between()函数填充了正弦和余弦曲线之间的区域,并为不同条件下的区域设置了不同的颜色和zorder。线条的zorder被设置为3,而刻度线的zorder被设置为4,确保它们显示在所有其他元素之上。
4.3 与散点图(Scatter)的交互
散点图是另一种常见的图表类型。通过调整散点和刻度线的zorder,我们可以控制它们的显示优先级。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Scatter and Ticks zorder Interaction")
# 生成数据
np.random.seed(42)
x = np.random.rand(50)
y = np.random.rand(50)
colors = np.random.rand(50)
sizes = 1000 * np.random.rand(50)
# 绘制散点图
scatter = ax.scatter(x, y, c=colors, s=sizes, alpha=0.6, zorder=2)
# 添加趋势线
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
ax.plot(x, p(x), "r--", zorder=1)
# 设置刻度线的zorder
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(3)
# 添加颜色条
plt.colorbar(scatter)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
plt.show()
Output:
在这个例子中,我们创建了一个散点图,其中点的大小和颜色都是随机的。我们还添加了一条趋势线,并将其zorder设置为1,使其位于散点之下。刻度线的zorder被设置为3,确保它们显示在所有其他元素之上。
5. set_zorder()的高级应用
除了基本的用法外,set_zorder()还有一些高级应用,可以帮助我们创建更复杂和精美的图表。
5.1 动态调整zorder
有时,我们可能需要根据数据或用户交互来动态调整元素的zorder。以下是一个示例,展示如何根据数据值动态设置散点的zorder:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Dynamic zorder Adjustment")
# 生成数据
np.random.seed(42)
x = np.random.rand(100)
y = np.random.rand(100)
values = np.random.rand(100)
# 根据值设置zorder
zorders = (values * 10).astype(int)
# 绘制散点图
scatter= ax.scatter(x, y, c=values, s=100, cmap='viridis', zorder=zorders)
# 设置刻度线的zorder为最高
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(11)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
plt.colorbar(scatter, label="Value (determines zorder)")
plt.show()
在这个例子中,我们根据每个点的值动态设置其zorder。这样,具有较高值的点会显示在具有较低值的点之上。刻度线的zorder被设置为11,确保它们始终显示在最上层。
5.2 分组设置zorder
在某些情况下,我们可能想要为不同组的元素设置不同的zorder。这在绘制多组数据时特别有用。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Group-based zorder Setting")
# 生成数据
np.random.seed(42)
groups = ['A', 'B', 'C']
colors = ['red', 'green', 'blue']
zorders = [1, 2, 3]
for group, color, zorder in zip(groups, colors, zorders):
x = np.random.rand(20)
y = np.random.rand(20)
ax.scatter(x, y, c=color, label=group, s=100, alpha=0.6, zorder=zorder)
# 设置刻度线的zorder为最高
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(4)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.legend()
plt.show()
Output:
在这个例子中,我们为三个不同的组设置了不同的颜色和zorder。这样,组C的点会显示在组B的点之上,而组B的点会显示在组A的点之上。
5.3 结合clip_on属性
set_zorder()方法可以与set_clip_on()方法结合使用,以创建超出轴范围的元素。这在创建自定义图例或标注时特别有用。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Combining zorder with clip_on")
# 生成数据
x = np.linspace(0, 10, 100)
y = np.sin(x)
# 绘制主曲线
ax.plot(x, y, 'b-', linewidth=2, label='Sin curve')
# 添加一个超出轴范围的矩形
rect = plt.Rectangle((11, -0.5), 1, 1, facecolor='red', alpha=0.5, zorder=3)
ax.add_patch(rect)
rect.set_clip_on(False)
# 添加一个超出轴范围的文本
ax.text(11.5, 0, "Outside\nAxis", ha='center', va='center',
bbox=dict(facecolor='yellow', alpha=0.5), zorder=4)
ax.text(11.5, 0, "Outside\nAxis", ha='center', va='center',
bbox=dict(facecolor='yellow', alpha=0.5), zorder=4).set_clip_on(False)
# 设置刻度线的zorder
for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks():
tick.set_zorder(2)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.legend()
plt.show()
Output:
在这个例子中,我们创建了一个超出轴范围的矩形和文本。通过设置clip_on=False和较高的zorder,这些元素可以显示在轴范围之外,并位于其他元素之上。
6. set_zorder()的性能考虑
虽然set_zorder()是一个强大的工具,但过度使用它可能会影响图表的渲染性能,特别是在处理大量数据点或复杂图表时。以下是一些使用set_zorder()时需要考虑的性能因素:
最小化zorder变化:尽量减少图表中不同的zorder值的数量。批量设置:如果可能,一次性为多个元素设置zorder,而不是单独设置。避免动态更改:频繁动态更改zorder可能会导致性能下降。
让我们看一个例子,展示如何高效地使用set_zorder():
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Efficient use of set_zorder()")
# 生成大量数据点
np.random.seed(42)
n_points = 10000
x = np.random.rand(n_points)
y = np.random.rand(n_points)
# 创建散点图,一次性设置所有点的zorder
scatter = ax.scatter(x, y, c=np.random.rand(n_points), s=10, alpha=0.5, zorder=2)
# 添加一条参考线
ax.axhline(y=0.5, color='r', linestyle='--', zorder=1)
# 批量设置刻度线的zorder
ax.xaxis.set_zorder(3)
ax.yaxis.set_zorder(3)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
plt.show()
Output:
在这个例子中,我们一次性为所有散点设置了zorder,并使用ax.xaxis.set_zorder()和ax.yaxis.set_zorder()方法批量设置刻度线的zorder,而不是遍历每个刻度线。这种方法在处理大量数据点时更加高效。
7. 常见问题和解决方案
使用set_zorder()时可能会遇到一些常见问题。以下是一些问题及其解决方案:
7.1 zorder设置不生效
有时,你可能会发现设置的zorder似乎没有生效。这通常是因为其他属性覆盖了zorder设置。
解决方案:检查是否有其他属性(如alpha或visible)影响了元素的显示。确保所有相关元素都正确设置了zorder。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Ensuring zorder Effectiveness")
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 正确设置zorder和alpha
ax.plot(x, y1, 'r-', linewidth=2, zorder=2, alpha=0.7, label='Sin')
ax.plot(x, y2, 'b-', linewidth=2, zorder=1, alpha=0.7, label='Cos')
# 设置刻度线的zorder
ax.xaxis.set_zorder(3)
ax.yaxis.set_zorder(3)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.legend()
plt.show()
Output:
7.2 图例顺序与zorder不一致
有时,图例中元素的顺序可能与它们在图表中的zorder不一致。
解决方案:使用legend()函数的reverse参数或手动调整图例顺序。
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(figsize=(10, 6))
ax.set_title("How2matplotlib.com - Aligning Legend with zorder")
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
line1 = ax.plot(x, y1, 'r-', linewidth=2, zorder=2, label='Sin')[0]
line2 = ax.plot(x, y2, 'b-', linewidth=2, zorder=1, label='Cos')[0]
# 手动调整图例顺序
handles, labels = ax.get_legend_handles_labels()
ax.legend([handles[1], handles[0]], [labels[1], labels[0]])
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
plt.show()
Output:
7.3 3D图表中的zorder问题
在3D图表中,zorder可能不会按预期工作,因为3D渲染使用不同的深度排序机制。
解决方案:对于3D图表,考虑使用sort_zpos参数来控制绘制顺序。
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(10, 6))
ax = fig.add_subplot(111, projection='3d')
ax.set_title("How2matplotlib.com - 3D zorder Workaround")
# 生成数据
x = np.random.rand(100)
y = np.random.rand(100)
z = np.random.rand(100)
# 使用sort_zpos来控制绘制顺序
scatter = ax.scatter(x, y, z, c=z, cmap='viridis', s=50, sort_zpos=True)
ax.set_xlabel("X-axis (how2matplotlib.com)")
ax.set_ylabel("Y-axis (how2matplotlib.com)")
ax.set_zlabel("Z-axis (how2matplotlib.com)")
plt.colorbar(scatter)
plt.show()
8. 总结
matplotlib.axis.Tick.set_zorder()函数是Matplotlib库中一个强大而灵活的工具,它允许我们精确控制图表元素的绘制顺序。通过巧妙运用这个函数,我们可以创建出层次分明、美观大方的数据可视化作品。
本文详细介绍了set_zorder()的使用方法、与其他绘图元素的交互、高级应用以及性能考虑。我们还讨论了使用这个函数时可能遇到的常见问题及其解决方案。
关键要点总结:
zorder值越大,元素绘制的层级越高。set_zorder()可以应用于多种图表元素,包括线条、散点、填充区域和刻度线。动态调整zorder可以创建交互式图表效果。结合clip_on属性,可以创建超出轴范围的元素。在使用set_zorder()时需要考虑性能影响,尤其是在处理大量数据时。3D图表中zorder的行为可能与2D图表不同,需要使用替代方法。
通过掌握set_zorder()函数,你将能够更好地控制Matplotlib图表的视觉层次,创造出更专业、更有吸引力的数据可视化作品。无论是简单的线图还是复杂的多元素图表,恰当使用zorder都能极大提升图表的清晰度和美观度。
希望本文能够帮助你更好地理解和运用matplotlib.axis.Tick.set_zorder()函数,为你的数据可视化之旅添砖加瓦。记住,创建优秀的数据可视化不仅需要准确的数据和适当的图表类型,还需要细致的视觉设计 —— 而set_zorder()正是实现这一目标的有力工具之一。
集成AD域,前端实现免登录
javaweb应用程序与AD域身份认证
首先,要做统一身份认证,也就是说,要java应用程序去ad域验证这个人的用户名密码是否存在于ad域,这个比较简单,只有一个坑,直接上代码吧。
private final String FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
private final String URL = "ldap://127.0.0.1:389/";
public boolean validateUP(String username,String pwd)throws NamingException{
Hashtable hashtable = new Hashtable();
hashtable.put(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
hashtable.put(Context.PROVIDER_URL, URL);//服务器地址
hashtable.put(Context.SECURITY_AUTHENTICATION, "simple");
hashtable.put(Context.SECURITY_PRINCIPAL, username);//用户名
hashtable.put(Context.SECURITY_CREDENTIALS, pwd);//密码
return new InitialLdapContext(hashtable,null)!=null;
}
看上去挺简单,而且试了下好用,嗯…以为很顺利了,但是,用了半天时间之后,不好使了!纳尼?
于是就开始网上各种的找资源,随后用了下面的,这波终于稳了。
static String host = "172.16.7.11";// AD域IP,必须填写正确
static String domain = "@raffles.local";// 域名后缀,例.@noker.cn.com
static String port = "389"; // 端口,一般默认389
public boolean validateUP(String userName, String password) {
String url = new String("ldap://" + host + ":" + port);// 固定写法
String user = userName.indexOf(domain) > 0 ? userName : userName + domain;// 网上有别的方法,但是在我这儿都不好使,建议这么使用
Hashtable env = new Hashtable();// 实例化一个Env
DirContext ctx = null;
env.put(Context.SECURITY_AUTHENTICATION, "simple");// LDAP访问安全级别(none,simple,strong),一种模式,这么写就行
env.put(Context.SECURITY_PRINCIPAL, user); // 用户名
env.put(Context.SECURITY_CREDENTIALS, password);// 密码
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");// LDAP工厂类
env.put(Context.PROVIDER_URL, url);// Url
try {
ctx = new InitialDirContext(env);// 初始化上下文
System.out.println("身份验证成功!");
return true;
} catch (AuthenticationException e) {
System.out.println("身份验证失败!");
e.printStackTrace();
} catch (javax.naming.CommunicationException e) {
System.out.println("AD域连接失败!");
e.printStackTrace();
} catch (Exception e) {
System.out.println("身份验证未知异常!");
e.printStackTrace();
} finally {
if (null != ctx) {
try {
ctx.close();
ctx = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return false;
}
好的,统一身份认证做好了,简单、舒服…接下来是自动登录了。
javaweb应用自动登录(免登录)
客户方有一个需求,大概意思就是要登录他们域内账户的电脑之后,不需要输入用户名密码,可以直接登入我们的系统。
听起来似乎很简单,做起来想简单也可以很简单。
第一版开始activeX:
既然是已经登录windows电脑了,那我只需要取到当前登录windows的这个人是谁就可以了,这是正常程序员思维,没毛病吧?
于是,通过查找资料,发现确实可以做到,但是只有ie能做到,并且需要启用activeX插件。类似于这样:
设置步骤:在“IE-Internet选项-安全-自定义级别-ActiveX控件和插件-对未标记为可安全执行脚本的ActivesX控件”,设置为“提示”或“启用”。
这种办法最简单,最容易,代码直接前端js获取就可以,
很快,具体细节实现就不写了,无非就是进入登录页的时候,就获取用户名,然后拿着用户名去我们的系统匹配用户,如果存在该用户就加载权限等信息,然后登录成功。
唰唰唰…唰,搞定收工!
然后客户不想设置这东西,我靠,白做了,还好没费啥劲。
那就继续找办法吧。
第二版开始jcifs:
对于这个获取的过程,已经有实现过的插件了,在网上搜了下,我用的是jcifs-1.3.19.jar,然后不需要别的了,就可以自动获取到用户名了。
首先配置web.xml:
MyNtlmHttpFilter
jcifs.http.NtlmHttpFilter
jcifs.http.domainController
172.16.7.11
jcifs.util.loglevel
6
MyNtlmHttpFilter
/*
配置也很好理解,就是一个过滤器,初始化参数一个是域地址ip,一个是日志级别。ip改成自己的就好。
然后,页面代码:通过就可以在页面端直接获取了,然后通过判断是否获取到来进行自动登录。
当然,因为我是试用的阶段是这样做的,如果这个jar实际可用的话,我就会写个过滤器,过滤一些路径,然后通过后台request获取用户名进行判断了…
都配好了,代码也写个差不多了,测一测,然后刚上来就报了个错
jcifs.smb.SmbException: The parameter is incorrect.
…
JCIFS的HttpFilter并不支持NTLM2协议,而当客户端是WIN7以上系统时,默认采用的是NTLM2协议。如果此时域控服务器也支持NTLM2,则会默认采用NTLM2协议验证。
在网上找了个办法,改客户机注册表。
REGEDIT4
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa]
“LMCompatibilityLevel”=dword:00000001
将上面的代码保存为一个.reg的文件。然后双击运行一下。ok通了。
然而。。。下面我们来说第三版吧。。。
第三版开始Waffle
当时是在这里: 看到的,waffle首选唉!于是下载了好几个版本,然后去试。
下载地址:
在smples文件夹下,我貌似发现了春天。。。于是开始看例子,然后自己往项目里配置。
web.xml开始:
SecurityFilter
waffle.servlet.NegotiateSecurityFilter
principalFormat
fqn
roleFormat
both
allowGuestLogin
true
securityFilterProviders
waffle.servlet.spi.NegotiateSecurityFilterProvider
waffle.servlet.spi.BasicSecurityFilterProvider
waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols
Negotiate
NTLM
waffle.servlet.spi.BasicSecurityFilterProvider/realm
WaffleFilterDemo
SecurityFilter
/*
WaffleInfo
waffle.servlet.WaffleInfoServlet
WaffleInfo
/waffle
然后同样用来获取用户名,ok启动项目,完美搞定,于是升级测试系统,我*******好吧。
看下图:
Waffle在Linux(类UNIX)上不起作用???
我真的是不服。。。继续
第四版开始记ip
本人想,既然是自动登录,我记录ip和用户名总可以的吧,然后下次这个人就免登录啊。于是说干就干,简单做了下,
下面是后台获取ip的代码:
public static String getIp2(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
System.out.println("x-forwarded-for ip: " + ip);
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个ip值,第一个ip才是真实ip
if( ip.indexOf(",")!=-1 ){
ip = ip.split(",")[0];
}
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
System.out.println("Proxy-Client-IP ip: " + ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
System.out.println("WL-Proxy-Client-IP ip: " + ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
System.out.println("HTTP_CLIENT_IP ip: " + ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
System.out.println("HTTP_X_FORWARDED_FOR ip: " + ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
System.out.println("X-Real-IP ip: " + ip);
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
System.out.println("getRemoteAddr ip: " + ip);
}
System.out.println("获取客户端ip: " + ip);
return ip;
}
但是,这种办法就不是一个办法,因为例如,同一个计算机,有多个人登录,第一个人登录之后,记录ip和账号,下次登录就免登录了,但第二个人也用这台电脑,然后他刚一访问,就进到第一个人登录之后的页面了。
第五版jespa
目前来讲,是纯java实现的最好解决方案,但是它收费,免费试用两个月。于是我就没怎么研究。
而且,我只知道它有一点,就是必须要运行他们提供的一个文件,用来在域内生成一个管理员的用户名密码,对于这种,我有点hold不住。
因为我是想不到什么办法去让他们运行那个文件。。。ps:人家想万一是病毒呢?漏洞呢?…唉,一般客户都比较小心眼的,而且收费软件人家也不想用。
最终版.net版:
因为.net是可以直接开启域验证的,这是java所不能比的。
所以,利用iframe嵌入.net的域验证,通过.net给返回的用户名实现自动登录。
问题:涉及到跨域,以及.net程序代码。
1、.net获取当前登录域用户名,可以参考:
2、来说iframe跨域问题:
对于我java的登录页,ssologin.jsp
然后,在加入ADLoginFrame.jsp
Insert title here
iframe跨域,大概就是登录页嵌入.net验证页面地址,然后.net验证后返回给ADLoginFrame.jsp,然后ADLoginFrame.jsp中访问parent.parent.setADDomainUser(text);
到时效果就是第一次弹窗让输入域用户名密码,第二次就不用输入了,直接自动登录。