`
whp0731
  • 浏览: 174761 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

spring security 学习 续一

阅读更多
 六、为了使用MD5对密码加密,我们需要修改一下配置文件。
任何一个正式的企业应用中,都不会在数据库中使用明文来保存密码的,我们在之前的章节中都是为了方便起见没有对数据库中的用户密码进行加密,这在实际应用中是极为幼稚的做法。可以想象一下,只要有人进入数据库就可以看到所有人的密码,这是一件多么恐怖的事情,为此我们至少要对密码进行加密,这样即使数据库被攻破,也可以保证用户密码的安全。
最常用的方法是使用MD5算法对密码进行摘要加密,这是一种单项加密手段,无法通过加密后的结果反推回原来的密码明文。

<authentication-provider>
    <password-encoder hash="md5"/>
    <jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>
        
盐值加密
<authentication-provider>
    <password-encoder hash="md5">
        <salt-source user-property="username"/>
    </password-encoder>
    <jdbc-user-service data-source-ref="dataSource"/>
</authentication-provider>
        
在password-encoder下添加了salt-source,并且指定使用username作为盐值。
盐值的原理非常简单,就是先把密码和盐值指定的内容合并在一起,再使用md5对合并后的内容进行演算,这样一来,就算密码是一个很常见的字符串,再加上用户名,最后算出来的md5值就没那么容易猜出来了。因为攻击者不知道盐值的值,也很难反算出密码原文。

七、 用户信息缓存
介于系统的用户信息并不会经常改变,因此使用缓存就成为了提升性能的一个非常好的选择。Spring Security内置的缓存实现是基于ehcache的,为了启用缓存功能,我们要在配置文件中添加相关的内容。

<authentication-provider>
    <password-encoder hash="md5">
        <salt-source user-property="username"/>
    </password-encoder>
    <jdbc-user-service data-source-ref="dataSource" cache-ref="userCache"/>
</authentication-provider>
        
我们在jdbc-user-service部分添加了对userCache的引用,它将使用这个bean作为用户权限缓存的实现。对userCache的配置如下所示:

<beans:bean id="userCache" class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache">
    <beans:property name="cache" ref="userEhCache"/>
</beans:bean>

<beans:bean id="userEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <beans:property name="cacheManager" ref="cacheManager"/>
    <beans:property name="cacheName" value="userCache"/>
</beans:bean>

<beans:bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
        
EhCacheBasedUserCache是Spring Security内置的缓存实现,它将为jdbc-user-service提供缓存功能。它所引用的userEhCache来自spring提供的EhCacheFactoryBean和EhCacheManagerFactoryBean,对于userCache的缓存配置放在ehcache.xml中:

<ehcache>
    <diskStore path="java.io.tmpdir"/>

    <defaultCache
        maxElementsInMemory="1000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
    />

    <cache
        name="userCache"
        maxElementsInMemory="100"
        eternal="false"
        timeToIdleSeconds="600"
        timeToLiveSeconds="3600"
        overflowToDisk="true"
    />
</ehcache>
        
 cache解释:
 内存中最多存放100个对象。
 不是永久缓存。 
 最大空闲时间为600秒。
 最大活动时间为3600秒。
 如果内存对象溢出则保存到磁盘。
 
八、获取当前用户信息
如果只是想从页面上显示当前登陆的用户名,可以直接使用Spring Security提供的taglib。

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<div>username : <sec:authentication property="name"/></div>
        
如果想在程序中获得当前登陆用户对应的对象。

UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
    .getAuthentication()
    .getPrincipal();
        
如果想获得当前登陆用户所拥有的所有权限。

GrantedAuthority[] authorities = userDetails.getAuthorities();
        
关于UserDetails是如何放到SecuirtyContext中去的,以及Spring Security所使用的TheadLocal模式,我们会在后面详细介绍。这里我们已经了解了如何获得当前登陆用户的信息。

九、自定义访问拒绝页面
在我们的例子中,user用户是不能访问/admin.jsp页面的,当我们使用user用户登录系统之后,访问/admin.jsp时系统默认会返回403响应。
如果我们希望自定义访问拒绝页面,只需要随便创建一个jsp页面,让后将这个页面的位置放到配置文件中。

下面创建一个accessDenied.jsp

<%@ page contentType="text/html;charset=UTF-8"%>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Access Denied</title>
    <style type="text/css">
div.error {
    width: 260px;
    border: 2px solid red;
    background-color: yellow;
    text-align: center;
}
    </style>
  </head>
  <body>
    <h1>Access Denied</h1>
    <hr>
    <div class="error">
      访问被拒绝<br>
      ${requestScope['SPRING_SECURITY_403_EXCEPTION'].message}
    </div>
    <hr>
  </body>
</html>

    
下一步修改配置文件,添加自定义访问拒绝页面的地址。

<http auto-config='true' access-denied-page="/accessDenied.jsp">
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
</http>
    
十、动态管理资源结合自定义登录页面
如果想将动态管理资源与自定义登录页面一起使用,最简单的办法就是在数据库中将登录页面对应的权限设置为IS_AUTHENTICATED_ANONYMOUSLY。

因此在数据库中添加一条资源信息。

INSERT INTO RESC VALUES(1,'','URL','/login.jsp*',1,'')
    
这里的/login.jsp*就是我们自定义登录页面的地址。

然后为匿名用户添加一条角色信息:

INSERT INTO ROLE VALUES(3,'IS_AUTHENTICATED_ANONYMOUSLY','anonymous')
    
最后为这两条记录进行关联即可。

INSERT INTO RESC_ROLE VALUES(1,3)
    
这样就实现了将动态管理资源与自定义登录页面进行结合。


十一、后登陆的将先登录的踢出系统 vs 后面的用户禁止登陆
默认情况下,后登陆的用户会把先登录的用户踢出系统。
想测试一下的话,先打开firefox使用user/user登陆系统,然后再打开ie使用user/user登陆系统。这时ie下的user用户会登陆成功,进入登陆成功页面。而firefox下的用户如何刷新页面,就会显示如下信息:
This session has been expired (possibly due to multiple concurrent logins being attempted as the same user).           
这是因为先登录的用户已经被强行踢出了系统,如果他再次使用user/user登陆,ie下的用户也会被踢出系统了。


后面的用户禁止登陆
如果不想让之前登录的用户被自动踢出系统,需要为concurrent-session-control设置一个参数。
<http auto-config='true'>
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
    <concurrent-session-control exception-if-maximum-exceeded="true"/>
</http>
            
这个参数用来控制是否在会话数目超过最大限制时抛出异常,默认值是false,也就是不抛出异常,而是把之前的session都销毁掉,所以之前登陆的用户就会被踢出系统了。

现在我们把这个参数改为true,再使用同一个账号同时登陆一下系统,看看会发生什么现象。
很好,现在只要有一个人使用user/user登陆过系统,其他人就不能再次登录了。这样可能出现一个问题,如果有人登陆的时候因为某些问题没有进行logout就退出了系统,那么他只能等到session过期自动销毁之后,才能再次登录系统。

十二、单点登录;略。

十三、为不同用户显示各自的登陆成功页面
一个常见的需求是,普通用户登录之后显示普通用户的工作台,管理员登陆之后显示后台管理页面。这个功能可以使用taglib解决。
其实只要在登录成功后的jsp页面中使用taglib判断当前用户拥有的权限进行跳转就可以。
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authorize ifAllGranted="ROLE_ADMIN">
  <%response.sendRedirect("admin.jsp");%>
</sec:authorize>
<sec:authorize ifNotGranted="ROLE_ADMIN">
  <%response.sendRedirect("user.jsp");%>
</sec:authorize>      
 当用户拥有ROLE_ADMIN权限时,既跳转到admin.jsp显示管理后台。
 当用户没有ROLE_ADMIN权限时,既跳转到user.jsp显示普通用户工作台。
这里我们只做最简单的判断,只区分当前用户是否为管理员。可以根据实际情况做更加复杂的跳转,当用户具有不同权限时,跳到对应的页面,甚至可以根据用户username跳转到各自的页面

十四、匿名登录
匿名登录,即用户尚未登录系统,系统会为所有未登录的用户分配一个匿名用户,这个用户也拥有自己的权限,不过他是不能访问任何被保护资源的。

设置一个匿名用户的好处是,我们在进行权限判断时,可以保证SecurityContext中永远是存在着一个权限主体的,启用了匿名登录功能之后,我们所需要做的工作就是从SecurityContext中取出权限主体,然后对其拥有的权限进行校验,不需要每次去检验这个权限主体是否为空了。这样做的好处是我们永远认为请求的主体是拥有权限的,即便他没有登录,系统也会自动为他赋予未登录系统角色的权限,这样后面所有的安全组件都只需要在当前权限主体上进行处理,不用一次一次的判断当前权限主体是否存在。这就更容易保证系统中操作的一致性。

配置文件
在配置文件中使用auto-config="true"就会启用匿名登录功能。在启用匿名登录之后,如果我们希望允许未登录就可以访问一些资源,可以在进行如下配置。

<http auto-config='true'>
    <intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
</http>
        
在access中指定IS_AUTHENTICATED_ANONYMOUSLY后,系统就知道此资源可以被匿名用户访问了。当未登录时访问系统的“/”,就会被自动赋以匿名用户的身份。我们可以使用taglib获得用户的权限主体信息。

这里的IS_AUTHENTICATED_ANONYMOUSLY将会交由AuthenticatedVoter处理,内部会依据AuthenticationTrustResolver判断当前登录的用户是否是匿名用户。

<div>
  username : <sec:authentication property="name"/>
  |
  authorities: <sec:authentication property="authorities" var="authorities" scope="page"/>
<c:forEach items="${authorities}" var="authority">
  ${authority.authority}
</c:forEach>
</div>
        
当用户访问系统时,就会看到如下信息,这时他还没有进行登录。
这里显示的是分配给所有未登录用户的一个默认用户名roleAnonyMous,拥有的权限是ROLE_ANONYMOUS。我们可以看到系统已经把匿名用户当做了一个合法有效的用户进行处理,可以获得它的用户名和拥有的权限,而不需判断SecurityContext中是否为空。

实际上,我们完全可以把匿名用户像一个正常用户那样进行配置,我们可以在配置文件中直接使用ROLE_ANONYMOUS指定它可以访问的资源。

<http auto-config='true'>
    <intercept-url pattern="/" access="ROLE_ANONYMOUS" />
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
</http>
        
不过,为了更明显的将匿名用户与系统中的其他用户区分开,我们推荐在配置时尽量使用IS_AUTHENTICATED_ANONYMOUSLY来指定匿名用户可以访问的资源。


十五、使用JAAS机制
可以在Spring Security中使用JAAS机制进行用户的身份认证。

JAAS即Java Authentication and Authorization Service,它是JDK自带的一套专门用于处理用户认证和授权的标准API,Spring Security中可以使用API作为AuthenticationProvider处理用户认证与授权。

配置文件中,我们使用JaasAuthenticationProvider作为AuthenticationProvider。

<http>
    <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" />
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login/>
    <logout/>
</http>

<beans:bean id="jaasAuthenticationProvider"
    class="org.springframework.security.providers.jaas.JaasAuthenticationProvider">
    <custom-authentication-provider/>
    <beans:property name="loginConfig" value="/WEB-INF/login.conf" />
    <beans:property name="loginContextName" value="JAASTest" />
    <beans:property name="callbackHandlers">
        <beans:list>
            <beans:bean class="org.springframework.security.providers.jaas.JaasNameCallbackHandler" />
            <beans:bean class="org.springframework.security.providers.jaas.JaasPasswordCallbackHandler" />
        </beans:list>
    </beans:property>
    <beans:property name="authorityGranters">
        <beans:list>
            <beans:bean class="com.family168.springsecuritybook.ch117.AuthorityGranterImpl" />
        </beans:list>
    </beans:property>
</beans:bean>
    
注意不能在http标签中使用auto-config="true"或是在http标签中包含rememberMe,因为rememberMe需要引用userDetailsService,而在使用JaasAuthenticationProvider时,用户数据校验是交由LoginModule处理的,不会使用userDetailsService,所以rememberMe会抛出异常。

我们将JAAS所需的配置文件放在/WEB-INF/login.config。

JAASTest {
    com.family168.springsecuritybook.ch117.LoginModuleImpl required;
};
    
并在配置文件中指明使用JAASTest作为登陆上下文。

<beans:property name="loginContextName" value="JAASTest" />
    
现在要创建LoginModuleImpl用来处理用户登录。
package com.family168.springsecuritybook.ch117;
public class LoginModuleImpl implements LoginModule {

    private String password;
    private String user;
    private Subject subject;

    public boolean abort() throws LoginException {
        return true;
    }

    public boolean commit() throws LoginException {
        return true;
    }

    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
        this.subject = subject;

        try {
            TextInputCallback textCallback = new TextInputCallback("prompt");
            NameCallback nameCallback = new NameCallback("prompt");
            PasswordCallback passwordCallback = new PasswordCallback("prompt", false);

            callbackHandler.handle(new Callback[] {textCallback, nameCallback, passwordCallback});

            password = new String(passwordCallback.getPassword());
            user = nameCallback.getName();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public boolean login() throws LoginException {
        if (!user.equals("user")) {
            throw new LoginException("Bad User");
        }

        if (!password.equals("user")) {
            throw new LoginException("Bad Password");
        }

        subject.getPrincipals().add(new Principal() {
                public String getName() {
                    return "TEST_PRINCIPAL";
                }
            });

        subject.getPrincipals().add(new Principal() {
                public String getName() {
                    return "NULL_PRINCIPAL";
                }
            });

        return true;
    }

    public boolean logout() throws LoginException {
        return true;
    }
}
    
当用户登录成功时,会通过authorityGranters为权限主体授权,这一步也要自己实现AuthorityGranter接口。


package com.family168.springsecuritybook.ch117;

import java.security.Principal;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.providers.jaas.AuthorityGranter;

public class AuthorityGranterImpl implements AuthorityGranter {
    public Set grant(Principal principal) {
        Set rtnSet = new HashSet();

        if (principal.getName().equals("TEST_PRINCIPAL")) {
            rtnSet.add("ROLE_USER");
            rtnSet.add("ROLE_ADMIN");
        }

        return rtnSet;
    }
}
    
至此,JAAS与Spring Security结合进行认证授权的功能已经完成,每一步都要件功能写死在代码里,让人感觉很不舒服。

 

十六、保存登录之前的请求
经常会碰到一种情况,用户花费大量时间编辑信息,但是session超时失效导致用户自动退出系统,安全过滤器会强制用户再次登录,但这也会使用户提交的信息全部丢失。

为了解决这个问题,Spring Security提供了一种称作SavedRequest功能,可以在未登录用户访问资源时,将用户请求保存起来,当用户登录成功之后SecurityContextHolderAwareRequestFilter会使用之前保存的请求,结合当前用户的请求生成一个新的请求对象,而这个请求对象中就保存了用户登录之前提交的信息。

SavedRequest功能默认就会被Spring Security启用,不需任何配置就可以重用登陆之前请求提交的数据。

 

 

分享到:
评论

相关推荐

    网络硬盘_源代码(Struts 2+Hibernate+Spring实现)

    本项目是一个基于Java技术栈的网络硬盘应用,利用了Struts 2、Hibernate和Spring三大框架集成开发。这个源代码提供了完整的功能实现,对于想要学习或扩展此类应用的开发者来说,具有很高的参考价值和实用性。 首先...

    基于Springboot+Vue的个人云盘管理系统的设计源码案例设计.zip

    通过Spring Data JPA,我们可以方便地操作数据库,而Spring Security则用于实现系统的权限控制和认证功能,确保数据的安全性。 其次,前端采用了Vue.js这一现代化的前端框架,Vue的特点在于其响应式的数据绑定和...

    使用 Acegi 保护 Java 应用程序: 续一

    在这个"使用 Acegi 保护 Java 应用程序:续一"的主题中,我们将深入探讨 Acegi 的核心概念和如何将其集成到你的项目中。 首先,Acegi 提供了一种基于角色的访问控制(RBAC)模型,允许你为不同的用户分配不同的角色...

    基于SSM的音乐管理网站基于SSM框架整合的一个音乐管理网站项目

    1. **Spring框架**:Spring是Java企业级应用的核心框架,提供依赖注入(DI)和面向切面编程(AOP)等功能。在音乐管理网站中,Spring主要负责管理对象(如DAO、Service等),并通过IoC容器控制它们的生命周期和装配...

    使用 Acegi 保护 Java 应用程序: 续二

    1. **Acegi 安全框架介绍**:Acegi 是一个基于 Spring 的企业级身份验证和授权解决方案,它为 Java 应用程序提供了强大的安全控制。它允许开发者通过声明式方式定义访问控制规则,降低了实现安全机制的复杂性。 2. ...

    springboot+vue实现超大文件分片极速上传与下载完整前后端源码

    在本项目中,"springboot+vue实现超大文件分片极速上传与下载完整前后端源码" 是一个综合性的IT应用...对于学习者来说,这是一个很好的实战案例,可以深入了解Spring Boot和Vue.js的综合应用,以及文件处理的相关技术。

    springboot-webuploader.zip

    7. **安全性**:考虑到上传文件可能带来的安全风险(如上传恶意脚本),项目中可能使用了Spring Security来过滤非法文件类型,或者在后端进行文件类型检查和安全校验。 通过这个项目,你可以学习到如何在SpringBoot...

    springboot+vue前后端分离开发项目源码

    综上所述,这个项目涵盖了Spring Boot后端开发、Vue.js前端开发、大文件上传技术等多个方面,是学习和实践现代Web开发技术的一个宝贵资源。开发者可以通过研究源码,深入了解前后端分离模式下的文件上传实现,以及...

    基于ssm+jsp的学生公寓管理中心系统源码数据库.zip

    1. **Spring**:Spring是一个全面的Java企业级应用开发框架,它的核心是依赖注入(Dependency Injection,DI),通过控制反转实现了对象之间的解耦。Spring还提供了AOP(面向切面编程)功能,用于实现如日志、事务...

    java毕业设计&课设-电器租赁小程序.zip

    1. 用户管理:包括用户注册、登录、个人信息管理等功能,可能会使用Spring Security进行权限控制。 2. 租赁管理:涉及电器的上架、下架、库存管理,以及用户租赁、归还、续租的处理,这里可能会使用MyBatis或JPA进行...

    网上资源管理系统(ssm)-源码

    1. **Spring框架**:Spring作为核心容器,负责管理应用程序中的对象,包括依赖注入(DI)和面向切面编程(AOP)。DI使得系统更加灵活,易于测试和维护,而AOP则可以方便地实现日志记录、事务管理等跨切面的功能。 2...

    java车位租赁(ssm)系统.zip

    1. 用户管理:注册、登录、权限控制等,这部分通常会涉及Spring Security或者Shiro框架。 2. 车位管理:包括车位信息录入、查询、状态更新(如空闲、已租),这需要设计合适的数据库表结构,并编写对应的MyBatis映射...

    JSP基于SSM汽车出租管理系统设计源码案例设计.zip

    1. 用户管理:用户注册、登录、个人信息管理等功能,涉及Spring Security进行权限控制和认证。 2. 车辆管理:车辆信息录入、查询、编辑、删除,涉及到数据库操作,MyBatis在此环节中起到了关键作用,通过SQL语句实现...

    基于Java SpringBoot的云盘.zip

    1. **SpringBoot框架**:SpringBoot是Spring框架的扩展,简化了Spring应用的初始搭建以及开发过程。它集成了大量常用的第三方库配置,如数据源、JPA、定时任务等,使得开发者能够快速创建独立运行的、生产级别的基于...

    candytotal检验

    在“CandyTotal”项目中,用户管理模块可能涉及到用户注册、登录、权限验证等功能,这需要利用Spring Security进行权限控制,确保用户信息的安全。而上传下载模块则可能使用到Spring的文件上传组件,结合Struts2的...

    Java教育课程管理系统源码.zip

    Java的安全框架如Spring Security或Apache Shiro可以实现用户登录验证、角色授权等功能,确保数据的安全性。 8. **文件上传与下载**: 课程资源的上传和下载功能可能涉及文件系统的操作。Java的File类以及流处理...

    -Springboot致远汽车租赁系统.zip

    2. 安全认证:Spring Security或JWT(JSON Web Tokens)用于用户身份验证和授权,确保系统安全。 3. 自动配置:Springboot的自动配置功能大大减少了配置文件的工作量,如数据库连接、定时任务等。 4. RESTful API:...

    毕业设计:基于SpringBoot的在线教育系统.zip

    1. **SpringBoot核心概念** - SpringBoot是由Pivotal团队提供的全新框架,其设计目标是简化Spring应用的初始搭建以及开发过程。 - 自动配置:SpringBoot通过“约定优于配置”的原则,自动配置了许多常用的Spring...

Global site tag (gtag.js) - Google Analytics