现在很多企业和开发团队都使用了SSH2(Struts 2 +Spring 2.5 +Hibernate)框架来进行开发, 我们或许已经习惯了强大的Spring Framework 全局配置管理,不可否认,Sping是一个很优秀的开源框架,但是由于Spring3.0版本后强大的的注解式bean的诞生,Spring MVC框架这匹黑马正悄然杀起,但今天Spring MVC不是主角,今天我和大家分享一个同样隶属于SpringSource 的安全框架——Spring Security, 下面的基于Spring MVC给大家分享一下Spring Security 的使用。虽然对它的接触时间不长,参考了一些网上朋友的做法,但也按照我的理解把这个框架介绍介绍,不是很专业,还请大家不要介意 。
我们知道,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中 的合法主体,也就是说用户能否访问该系统。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个 资源来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
首先,我们看web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- 编码统一最好放最上面,最先加载,防止乱码--> <filter> <filter-name>Set Character Encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value><!-- 强制进行转码 --> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 然后接着是SpringSecurity必须的filter 优先配置,让SpringSecurity先加载,防止SpringSecurity拦截失效--> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- spring需要加载的配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> WEB-INF/classes/applicationContext.xml, WEB-INF/spring3-servlet.xml, WEB-INF/spring-security.xml </param-value> </context-param> <listener> <listener-class> <!-- 所以,要在web.xml下面配置好监听,让服务器启动时就初始化改类,可以得到request --> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 默认所对应的配置文件是WEB-INF下的{servlet-name}-servlet.xml,这里便是:spring3-servlet.xml --> <servlet> <servlet-name>spring3</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring3</servlet-name> <!-- 这里可以用 / 但不能用 /* ,拦截了所有请求会导致静态资源无法访问,所以要在spring3-servlet.xml中配置mvc:resources --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
注释已经写了挺多,还是稍微解释一下要注意的地方,一个是UTF-8编码转换,这个最好加在最前面,让它先生效,我在调试的时候就出过这种情 况,web.xml里的其他配置都正常生效了,但是编码死活不行,一中文就乱码,郁闷了老半天,然后突发奇想,是不是web.xml里先声明的配置先生 效,后声明的后生效?接着实践,果然不出我所料,把编码转换加在前面,一切正常。。。。我那个晕。。。
关于Spirng MVC的就不说了,那些数据访问、业务和控制层那些的东东就自个研究去了吧。。这不是今天的重点。
接着是spring-security.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"> <!-- Spring-Security 的配置 --> <!-- 注意use-expressions=true.表示开启表达式,否则表达式将不可用. see:http://www.family168.com/tutorial/springsecurity3/html/el-access.html --> <security:http auto-config="true" use-expressions="false" access-denied-page="/user/login_failure.html"> <!--允许所有人访问--> <!-- <security:intercept-url pattern="/**" access="permitAll" />--> <!--允许ROLE_ADMIN权限访问--> <security:intercept-url pattern="/user/findAll.html" access="ROLE_ADMIN" /> <!--允许ROLE_ADMIN权限访问--> <security:intercept-url pattern="/user/**" access="ROLE_ADMIN" /> <!--允许ROLE_USER权限访问--> <security:intercept-url pattern="/success.jsp" access="ROLE_USER,ROLE_ADMIN" /> <!--允许IS_AUTHENTICATED_ANONYMOUSLY匿名访问--> <security:intercept-url pattern="/anonymously.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <!-- filters="none" 不过滤这些资源--> <security:intercept-url pattern="/js/**" filters="none" /> <security:intercept-url pattern="/index.jsp" filters="none" /> <!-- login-page:默认指定的登录页面. authentication-failure-url:出错后跳转页面. default-target-url:成功登陆后跳转页面 --> <security:form-login login-page="/index.jsp" authentication-failure-url="/user/login_failure.html" default-target-url="/success.jsp" /> <!-- invalidate-session:指定在退出系统时是否要销毁Session。logout-success-url:退出系统后转向的URL。logout-url:指定了用于响应退出系统请求的URL。其默认值为:/j_spring_security_logout。 --> <security:logout invalidate-session="true" logout-success-url="/index.jsp" logout-url="/j_spring_security_logout" /> <!-- max-sessions:允许用户帐号登录的次数。范例限制用户只能登录一次。exception-if-maximum-exceeded: 默认为false,此值表示:用户第二次登录时,前一次的登录信息都被清空。当exception-if-maximum-exceeded="true"时系统会拒绝第二次登录。 --> <security:session-management> <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1" /> </security:session-management> </security:http> <!-- 指定一个自定义的authentication-manager :customUserDetailsService --> <security:authentication-manager> <security:authentication-provider user-service-ref="customUserDetailsService"> <security:password-encoder ref="passwordEncoder" /> </security:authentication-provider> </security:authentication-manager> <!-- 对密码进行MD5编码 --> <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" /> <!-- 通过 customUserDetailsService,Spring会控制用户的访问级别. 也可以理解成:以后我们和数据库操作就是通过customUserDetailsService来进行关联. --> <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" /> <!-- 自定义登陆错误提示,可以取出mymessages.properties的国际化消息--> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basename" value="classpath:org/yzsoft/springmvcdemo/mymessages" /> </bean> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" /> </beans>
这个多解释一下,首先
<security:intercept-url pattern="/findAll.html" access="hasRole('ROLE_ADMIN')" /> <security:intercept-url pattern="/user/**" access="hasRole('ROLE_ADMIN')" /> <security:intercept-url pattern="/anonymously.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
这个是权限控制,声明了拥有什么权限可以访问哪些资源,这个配置的是有ROLE_ADMIN权限的才可以访问/findAll.html,至于这 个ROLE_ADMIN从哪来,呆会再解释。或者像第二句一样配置也可以:拥有ROLE_ADMIN权限的才可以访问/user/下的所有资源,否则都会抛出AccessDeniedException 。
IS_AUTHENTICATED_ANONYMOUSLY就是匿名访问的意思,这个相信都懂的。。。
然后是登陆和安全退出
<security:form-login login-page="/index.jsp" authentication-failure-url="/user/login_failure.html"default-target-url="/user/findAll.html" /> <security:logout invalidate-session="true" logout-success-url="/index.jsp" logout-url="/user/login_failure.html" />
解释下上面一句,相信看也能看出来了的,login-page:默认指定的登录页面. authentication-failure-url:出错后跳转页面(包括那些个啥用户名密码错误吖。。啥啥啥的。). default-target-url:成功登陆后跳转页面 (这里我直接跳到的控制器去查数据列表)。
接着:invalidate-session:指定在退出系统时是否要销毁Session。logout-success-url:退出系统后转 向的URL。logout-url:指定了用于响应退出系统请求的URL。其默认值为:/j_spring_security_logout。
接下来是一个比较不错的功能:是否允许同一用户多处登陆
<security:session-management> <security:concurrency-control error-if-maximum-exceeded="true" max-sessions="1" /> </security:session-management>
exception-if-maximum-exceeded:
默认为false,此值表示:用户第二次登录时,前一次的登录信息都被清空。当error-if-maximum-exceeded="true"时系统会拒绝第二次登录。
max-sessions:允许用户帐号登录的次数,这里我们允许一次登陆。这里我们做个实验吧,看看是不是真的生效了,请看图
这里我们看到,当同一个账号多处登陆时,就会报出Maximum sessions of 1 for this principal exceeded 的错误,当然,正式使用我们换成mymessages.properties里的我们自定义的国际化消息,这样人性化一点。
好,我们往下看,接着就是应用我们实际项目里的自定义用户权限了
<security:authentication-manager> <security:authentication-provider user-service-ref="customUserDetailsService"> <security:password-encoder ref="passwordEncoder" /> </security:authentication-provider> </security:authentication-manager> <!-- 对密码进行MD5编码 --> <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" /> <bean id="customUserDetailsService" class="org.yzsoft.springmvcdemo.util.CustomUserDetailsService" />
首先是<security:authentication-manager>是指定我们自定义的身份验证策略,这里我们用 customUserDetailsService这个bean,就是指向我们CustomUserDetailsService.java这个类。然 后<security:password-encoder>指定我们密码使用MD5进行编码,调用Spring Security自带的MD5加密类。当然,还有加盐MD5或我们自己写的加密算法等安全性更加高的密码策略。这个按项目实际使用配置吧。
然后看到我们的CustomUserDetailsService.java
package org.yzsoft.springmvcdemo.util; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.yzsoft.springmvcdemo.serviceimpl.UsersServiceImpl; import org.yzsoft.springmvcdemo.vo.TUsers; /** * 一个自定义的类用来和数据库进行操作. 即以后我们要通过数据库保存权限.则需要我们继承UserDetailsService * * @author * */ public class CustomUserDetailsService implements UserDetailsService { protected static Logger logger = Logger.getLogger("service");//log4j,不用解释了吧。。 @Autowired private UsersServiceImpl usersService; public UsersServiceImpl getUsersService() { return usersService; } public void setUsersService(UsersServiceImpl usersService) { this.usersService = usersService; } public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { UserDetails user = null; try { // 搜索数据库以匹配用户登录名. // 我们可以通过dao使用Hibernate来访问数据库 System.out.println(username + " 用户页面输入的用户名"); TUsers tusers = this.usersService.findByUsername(username); System.out.println(tusers.getUsername() + " 数据库取出的用户名"); // Populate the Spring User object with details from the dbUser // Here we just pass the username, password, and access level // getAuthorities() will translate the access level to the correct // role type // 用户名、密码、是否启用、是否被锁定、是否过期、权限 user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole()))); } catch (Exception e) { logger.error("用户信息错误!"); throw new UsernameNotFoundException("异常处理:检索用户信息未通过!"); } return user; } /** * 获得访问角色权限列表 * * @param access * @return */ public Collection<GrantedAuthority> getAuthorities(Integer role) { System.out.println("取得的权限是 :" + role); List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); // 所有的用户默认拥有ROLE_USER权限 if (role == 0) { System.out.println("普通用户"); logger.debug("取得普通用户权限-->"); authList.add(new GrantedAuthorityImpl("ROLE_USERS")); } // 如果参数role为1.则拥有ROLE_ADMIN权限 if (role == 1) { logger.debug("取得ADMIN用户权限-->"); authList.add(new GrantedAuthorityImpl("ROLE_ADMIN")); } System.out.println(authList.size()+" 权限列表长度"); return authList; } }
这里就是把我们从数据库里面取得的用户权限和Spring Security的配置进行桥接,还记得上面配置文件里的ROLE_ADMIN吧,就是从这里来的,很奇怪的是,这个必须设置成ROLE_ 开头才有效。。郁闷。。。
这里我刚开始有一个疑惑,我们看这2句
TUsers tusers = this.usersService.findByUsername(username); user = new User(tusers.getUsername(), tusers.getPassword().toLowerCase(), true, true, true, true, getAuthorities(Integer.parseInt(tusers.getRole())));
这里根本不需要用户输入的密码,只要了用户名,然后直接根据用户名去取权限,就直接设置进Spring Security的User对象里面去,我不禁一身冷汗,这不相当于说有了用户名就直接去查数据库么,而且是不用密码的。。。。
但经过查看官方文档和网上的解释,这才放心,原来是这样的,Spring Security的确是直接根据用户名去查,但是查得出来的Spring Security User对象之后,它会根据这个对象的属性值去数据库查询与这个对象匹配的数据,我们这里设置的是(用户名,密码,是否启用、是否被锁定、是否过期、权 限。。。),那么如果数据库存在这个对象,就返回真,否则返回假,这样也就不用担心了,完全可靠。就是我们在前台要做好限制,不能给用户不输密码就访问, 不然挤爆你数据库连接。。。。。
最后登陆页面index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'index.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> 用户登陆 <br> ${SPRING_SECURITY_LAST_EXCEPTION.message} <form action="j_spring_security_check" method="post"> USERNAME:<input type="text" name="j_username" value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" /><br/> PASSWORD:<input type="password" name="j_password" value="" /><br/> <input type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆(这个功能没有做的)<br/> <input type="submit"> </form> </body> </html>
这里我还是使用Spring Security默认的j_username和j_password,表单目标也用默认的j_spring_security_check,会默认跳到Spring Security进行拦截。其他的应该不用解释了吧。。。。
最后 ,我们整理一下Spring Security的整个控制过程:
——>用户登陆
——> <security:authentication-manager> 拦截
——>交给customUserDetailsService处理,并且声明密码采用MD5策略
——>根据输入的用户名去数据库查这条记录,验证身份
——>取出该条记录的用户权限(锁定、禁用、过期和实际权限等)
——>根据取得的权限列表去security:intercept-url匹配、授权,然后判断是否放行。
这就完成了一整个的权限控制流程。
接下来我们来测试一下看是否真的生效了:
1、测试匿名访问页面,直接地址栏访问:
2、普通用户登陆
然后访问后台管理页面,跳回了登陆页
3、管理员用户登陆
退出后成功跳转回登陆页,点击后退再执行其他操作,这时候session已经注销了的,不能执行,又跳回了登陆页。是我们想要的效果,OK,成功了。
好了,Spring Security的简单使用就讲到这里,其实这只是Spring Security的一小部分,而且这里我还没有用权限表对用户权限进行专门的管理,很多东西还是用Spring Security 默认的,还有Spring Security CAS (单点登陆)以及更加高级的权限控制和更完善的Spring Security 配置,以后我们再慢慢去研究吧。发现Spring Security 这个技术不仅简化了我们的用户权限管理,要知道我们做管理系统的时候这是个大问题,也差不多颠覆了我一贯以来用户权限管理的观念,但是掌握了这种思维之 后,又发现,其实,程序并不是只有一种实现方式,它激发了我写程序时要去寻找多种解决方案的想法。学习的路上,就是要不断推翻自己固有的思维,去见识多种 新事物,才能有进步。
最后是项目文件,比较大,分卷了,还没有Maven的版本,以后奉上~~~
相关推荐
springsecurity1.2 引入SpringSecurity 2. 认证 2.1 登陆校验流程 2.2 原理初探 2.2.1 SpringSecurity完整流程 2.2.2 认证流程详解 2.3 解决问题 2.3.1 思路分析 2.3.2 准备工作 2.3.3 实现 2.3.3.1 数据库校验用户 ...
本手册主要讲解 SpringBlade 的开发手册,包括环境要求、环境准备、基础环境安装、Nacos 安装、Sentinel 安装、Seata 安装、插件安装、工程导入、工程运行、工程测试、开发初探、新建微服务工程、第一个 API、鉴权 ...
此外,书中还涉及了Spring Security,帮助开发者理解和实施应用的安全控制。 《Spring in Action》的最新版还可能涵盖了Spring Boot、Spring Cloud等现代Spring生态系统的组件,这些技术极大地简化了Spring应用的...
8. **Spring Security**:Spring提供的安全框架,用于身份验证和授权,如何配置以保护Web应用的安全。 9. **测试支持**:Spring提供的单元测试和集成测试工具,如Spring Test和Spring Boot Test,以及Mockito等库的...
- **开发初探**:通过一系列实例帮助用户理解SpringBlade的核心功能和使用方式,包括创建微服务工程、API开发等。 ##### 2. 微服务开发实践 - **第一个API**:以一个简单的示例来演示如何创建和使用API,包括API...
《Spring框架初探》 Spring框架是Java企业级应用开发中的重要组成部分,它以其轻量级、模块化和全面的特性赢得了广大开发者喜爱。本文将深入探讨Spring框架的基础知识,帮助初学者理解并掌握这一强大的工具。 首先...
从代理机制初探 AOP 动态代理 <br>AOP 观念与术语 Spring AOP Advices Advices 包括了Aspect 的真正逻辑,由于缝合至Targets的时机不同,Spring 提供了几种不同的 Advices。 Before ...
在最后一章,我们将探索Spring的一些高级特性,例如Spring Boot用于快速开发,Spring Cloud用于构建分布式系统,以及Spring Security进行权限控制和安全防护。此外,我们还将学习Spring Integration和Spring Batch...
- **安全控制**:Spring的安全框架(如Spring Security)可以与GWT应用集成,提供用户认证和授权功能。 - **数据持久化**:Spring Data JPA或其他ORM工具可以用来处理数据库操作,使GWT应用能方便地访问后端数据。 ...
这个名为"spring-boot-1"的项目很可能是对Spring Boot的初探或者是某个特定版本的示例代码库。虽然没有提供具体的标签,我们可以根据Spring Boot的核心特性来探讨相关的知识点。 1. **Spring Boot基础**: Spring ...
OAuth2.0初探 学习编写 REST 接口测试用例 学习自定义 validator 学习自定义 filter 学习自定义 interceptor 自定义 aspect 学习 REST 接口多线程编程 Thread#start Thread#stop spring boot 1.5.20.RELEASE spring ...
JSP和Servlet可以结合过滤器、安全框架(如Spring Security)来防止SQL注入、XSS攻击等,同时,系统需要保护用户的隐私数据,如使用HTTPS协议加密传输,以及对敏感操作进行身份验证和权限控制。 总的来说,这篇论文...
Spring Security提供了一套完整的安全解决方案,包括用户认证、权限控制等功能。 四、购物车与订单处理 1. **购物车实现**:购物车通常通过Session来存储用户选择的商品,当用户确认购买时,将购物车中的商品转化...
同时,配合Spring Security,可以实现权限控制和用户认证,为应用提供安全的访问环境。在这个项目中,我们可能会看到如何定义控制器处理HTTP请求,以及如何通过模型视图来传递数据到前端页面。 接下来,密码的安全...
安全性方面,Apache Shiro或Spring Security可能被用来处理用户认证和授权,确保系统安全。 总的来说,"sms_0.1.rar"是一个基于Java开发的学生管理系统,可能具备一套完整的用户管理和教学管理功能,其设计简洁,...
- **权限管理**:集成Spring Security或Shiro,实现细粒度的权限控制。 - **工作流引擎**:可能集成Activiti或Flowable,支持业务流程的建模和执行。 - **数据源管理**:支持多数据源配置,便于数据隔离和分布式部署...
6. **安全性**:考虑到电子商务涉及敏感的用户信息和交易数据,项目可能会采用Spring Security或者Apache Shiro等安全框架,实现身份验证、授权和加密等功能,确保系统安全。 7. **测试与持续集成**:在GitHub上的...