顶部左侧内容
百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 在线教程 > 正文

SpringMVC + security模块 框架整合详解

gosiye 2024-05-30 00:20 13 浏览 0 评论

最近整理一个二手后台管理项目,整体大体可分为 : Springmvc + mybatis + tiles + easyui + security 这几个模块,再用maven做管理。刚拿到手里面东西实在太多,所以老大让重新整一个,只挑出骨架。不可否认,架构整体规划得还是很好的,尤其是前端界面用tiles+easyui ,开发很高效!其实,之前虽然听说过security,但做过的项目都没用过security,所以也算是一个新手!整合过程还是很烧脑的。

1、首先是springmvc部分,也是框架的最核心骨架部分。springmvc也是基于servlet框架完成的,所以我们都可以从web.xml入手,然后顺藤摸瓜,整理出整个架构。web.xml中主要部分是放置spring的各种配置:


  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>
  4. /WEB-INF/spring/appServlet/servlet-context.xml
  5. </param-value>
  6. </context-param>


  1. <servlet>
  2. <servlet-name>appServlet</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <init-param>
  5. <param-name>contextConfigLocation</param-name>
  6. <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
  7. </init-param>
  8. <load-on-startup>1</load-on-startup>
  9. <async-supported>true</async-supported>
  10. </servlet>
  11. <servlet-mapping>
  12. <servlet-name>appServlet</servlet-name>
  13. <url-pattern>/</url-pattern>
  14. </servlet-mapping>

这两部分指定了配置文件地址和web请求地址拦截规则。需要注意的是,然后配置文件里就是常用的注解、数据源、之类的,不一而足。

2、然后是security,本文实现了自定义的用户信息登录认证,主要部分:spring的security主要注意几个地方:

1)web.xml中配置security必须的过滤器,注意名字必须是springSecurityFilterChain,这是内置的过滤器;然后将过滤规则设置为/*,表示对所有请求都拦截。

2) 关于security的配置文件必须在<context-param>中加入,因为ContextLoaderListener在加载的时候就会去加载security相关的东西,而此时springmvc(servlet)模块还没有初始化。

二者配置如下:


  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>
  4. /WEB-INF/spring/appServlet/servlet-context.xml
  5. </param-value>
  6. </context-param>
  7. <!-- Creates the Spring Container shared by all Servlets and Filters -->
  8. <listener>
  9. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  10. </listener>
  11. <!-- 用户权限模块 -->
  12. <filter>
  13. <filter-name>springSecurityFilterChain</filter-name>
  14. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  15. </filter>
  16. <filter-mapping>
  17. <filter-name>springSecurityFilterChain</filter-name>
  18. <url-pattern>/*</url-pattern>
  19. </filter-mapping>

本文中security配置文件再servlet-context.xml中被引入,形如:

<beans:import resource="spring-security.xml"/>

3)spring-security.xml配置文件:

现在来看这个xml文件:

首先 <http>标签部分声明了访问拦截规则,这是security的关键。在这部分,我们先声明了一些不需要验证的资源和访问路径;然后是地址拦截规则部分:该部分拦截路径是/**,**表示可以跨目录结构,因此此处拦截该站点所有请求(除之前声明security=“none”的以外),然后拦截规则access="isAuthenticated()",这是SecurityExpressionRoot中的判断是否认证过的方法之一,跟hasRole()类似,官方描述是Returns true if the user is not anonymous ,也就是用户认证后返回true。


  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans:beans xmlns="http://www.springframework.org/schema/security"
  3. xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:security="http://www.springframework.org/schema/security"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  7. http://www.springframework.org/schema/security
  8. http://www.springframework.org/schema/security/spring-security-3.2.xsd">
  9. <!-- For Web security -->
  10. <http pattern="/js/**" security="none"/>
  11. <http pattern="/images/**" security="none"/>
  12. <http pattern="/mgmt/**" security="none"/>
  13. <http pattern="/image_sys/**" security="none"/>
  14. <http pattern="/style/**" security="none"/>
  15. <http pattern="/view/login.jsp" security="none"/>
  16. <http pattern="/view/login/forgotPassword.jsp" security="none"/>
  17. <http pattern="/securityCodeImage.html" security="none"/>
  18. <http pattern="/checkSecurityCode.html" security="none"/>
  19. <http pattern="/admin/validateMobile.html" security="none"/>
  20. <http pattern="/verifycode/sendVerifyCode.html" security="none"/>
  21. <http pattern="/admin/resetPassword.html" security="none"/>
  22. <http use-expressions="true" entry-point-ref="authenticationProcessingFilterEntryPoint" access-denied-page="/view/403.jsp">
  23. <intercept-url pattern="/**" access="isAuthenticated()" />
  24. <remember-me />
  25. <!-- <expression-handler ref="webSecurityExpressionHandler"/> -->
  26. <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" />
  27. <logout logout-url="/j_spring_security_logout" logout-success-url="/view/login.jsp" delete-cookies="JSESSIONID"/>
  28. </http>
  29. <!-- 未登录的切入点 -->
  30. <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
  31. <beans:property name="loginFormUrl" value="/view/login.jsp"></beans:property>
  32. </beans:bean>
  33. <!-- 登录体系loginFilter -->
  34. <beans:bean id="loginFilter"
  35. class="com.security.AdminUsernamePasswordAuthenticationFilter">
  36. <beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property>
  37. <beans:property name="authenticationSuccessHandler" ref="myAuthenticationSuccessHandler"></beans:property>
  38. <beans:property name="authenticationFailureHandler" ref="myAuthenticationFailureHandler"></beans:property>
  39. <beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
  40. </beans:bean>
  41. <!-- 验证失败显示页面 -->
  42. <beans:bean id="myAuthenticationFailureHandler"
  43. class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
  44. <beans:property name="defaultFailureUrl" value="/view/login.jsp?error=true" />
  45. </beans:bean>
  46. <!-- 验证成功默认显示页面 -->
  47. <beans:bean id="myAuthenticationSuccessHandler"
  48. class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
  49. <beans:property name="alwaysUseDefaultTargetUrl" value="true" />
  50. <!--此处可以请求登录首页的action地址 -->
  51. <beans:property name="defaultTargetUrl" value="/index/homepage" />
  52. </beans:bean>
  53. <!-- authentication体系 -->
  54. <authentication-manager alias="authenticationManager" erase-credentials="false">
  55. <authentication-provider ref="authenticationProvider" />
  56. </authentication-manager>
  57. <beans:bean id="authenticationProvider"
  58. class="com.security.AdminAuthenticationProvider">
  59. <beans:property name="userDetailsService" ref="userDetailsService" />
  60. </beans:bean>
  61. <beans:bean id="userDetailsService"
  62. class="com.security.AdminUserDetailsService">
  63. <beans:property name="adminService" ref="adminServiceImpl"></beans:property>
  64. </beans:bean>

</beans:beans>

然后声明了自定义的用户登录的拦截器loginFilter,用于在登录时实现security认证。spring的登录认证模块执行顺序为:

1、UsernamePasswordAuthenticationFilter的attemptAuthentication()方法,此部分可以做用户名密码的判断,正确后继续执行;

2、接下来调用AuthenticationManager的authenticate()方法(中途具体怎么调用此处不深究),一个Mannager对应了多个authenticationProvider,其实最终是通过调用provider的authenticate()方法来进行认证的,只要provider的supports方法返回true即可声明该provider或可进行认证,最后将被manager调用。在authenticate()方法中,调用UserDetails的loadUserByUserName()方法来加载登录用户信息,包括权限信息,最后封装成AbstractAuthenticationToken,这个对象就是security模块认证后的用户完整信息。

自定义认证主要类如下:


  1. /**
  2. * 用户登录验证步骤一:attemptAutentication()
  3. */
  4. public class AdminUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
  5. public static final String VALIDATE_CODE = "validateCode";
  6. public static final String USERNAME = "j_username";
  7. public static final String PASSWORD = "j_password";
  8. public static final String EMPLOYEENO = "j_employeeNo";
  9. @Resource
  10. private AdminService adminService;
  11. @Override
  12. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  13. throws AuthenticationException {
  14. if (!request.getMethod().equals("POST")) {
  15. throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  16. }
  17. // 判断用户信息
  18. // UsernamePasswordAuthenticationToken实现 Authentication
  19. UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
  20. adminModel.getAdminId(), password);
  21. // 允许子类设置详细属性
  22. setDetails(request, authRequest);
  23. // 运行UserDetailsService的loadUserByUsername 再次封装Authentication
  24. return this.getAuthenticationManager().authenticate(authRequest);
  25. }
  26. @Override
  27. protected String obtainUsername(HttpServletRequest request) {
  28. Object obj = request.getParameter(USERNAME);
  29. return null == obj ? "" : obj.toString();
  30. }
  31. @Override
  32. protected String obtainPassword(HttpServletRequest request) {
  33. Object obj = request.getParameter(PASSWORD);
  34. return null == obj ? "" : obj.toString();
  35. }
  36. }

  1. /**
  2. * <span style="font-family: Arial, Helvetica, sans-serif;">用户登录验证步骤二:</span><span style="font-family: Arial, Helvetica, sans-serif;">authenticate</span><span style="font-family: Arial, Helvetica, sans-serif;">()</span><span style="font-family: Arial, Helvetica, sans-serif;">
  3. </span> */
  4. public class AdminAuthenticationProvider implements AuthenticationProvider {
  5. protected Logger logger = LoggerFactory.getLogger(this.getClass());
  6. private UserDetailsService userDetailsService = null;
  7. public AdminAuthenticationProvider() {
  8. super();
  9. }
  10. /**
  11. * @param userDetailsService
  12. */
  13. public AdminAuthenticationProvider(UserDetailsService userDetailsService) {
  14. super();
  15. this.userDetailsService = userDetailsService;
  16. }
  17. public UserDetailsService getUserDetailsService() {
  18. return userDetailsService;
  19. }
  20. public void setUserDetailsService(UserDetailsService userDetailsService) {
  21. this.userDetailsService = userDetailsService;
  22. }
  23. /**
  24. * provider的authenticate()方法,用于登录验证
  25. */
  26. @SuppressWarnings("unchecked")
  27. public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  28. // 1. Check username and password
  29. try {
  30. doLogin(authentication);
  31. } catch (Exception e) {
  32. if (e instanceof AuthenticationException) {
  33. throw (AuthenticationException) e;
  34. }
  35. logger.error("failure to doLogin", e);
  36. }
  37. // 2. Get UserDetails
  38. UserDetails userDetails = null;
  39. try {
  40. userDetails = this.userDetailsService.loadUserByUsername(authentication.getName());
  41. } catch (Exception e) {
  42. if (e instanceof AuthenticationException) {
  43. throw (AuthenticationException) e;
  44. }
  45. logger.error("failure to get user detail", e);
  46. }
  47. // 3. Check and get all of admin roles and contexts.
  48. Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) userDetails.getAuthorities();
  49. if (authorities != null && !authorities.isEmpty()) {
  50. AdminAuthenticationToken token = new AdminAuthenticationToken(authentication.getName(),
  51. authentication.getCredentials(), authorities);
  52. token.setDetails(userDetails);
  53. return token;
  54. }
  55. throw new BadCredentialsException("没有分配权限");
  56. }
  57. protected void doLogin(Authentication authentication) throws AuthenticationException {
  58. }
  59. @Override
  60. public boolean supports(Class<?> authentication) {
  61. // TODO Auto-generated method stub
  62. return true;
  63. }
  64. }

  1. /**用户详细信息获取
  2. */
  3. public class AdminUserDetailsService implements UserDetailsService {
  4. private Logger logger = LoggerFactory.getLogger(AdminUserDetailsService.class);
  5. private AdminService adminService;
  6. @Override
  7. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  8. AdminUserDetails userDetails = new AdminUserDetails();
  9. userDetails.setAdminId(username);
  10. ///加载用户基本信息
  11. AdminModel adminModel = adminService.getAdminByAdminId(username);
  12. try {
  13. PropertyUtils.copyProperties(userDetails, adminModel);
  14. } catch (IllegalAccessException e) {
  15. logger.error("用户信息复制到userDetails出错",e);
  16. } catch (InvocationTargetException e) {
  17. logger.error("用户信息复制到userDetails出错",e);
  18. } catch (NoSuchMethodException e) {
  19. logger.error("用户信息复制到userDetails出错",e);
  20. }
  21. //加载权限信息
  22. List<AdminRoleGrantedAuthority> authorities = this.adminService.getAuthorityByUserId(username);
  23. if (authorities == null || authorities.size() == 0) {////如果为普通用户
  24. if (isCommonUserRequest()) {
  25. AdminRoleGrantedAuthority authority =
  26. new AdminRoleGrantedAuthority(AdminRoleGrantedAuthority.ADMIN_ROLE_TYPE_COMMON_USER);
  27. userDetails.getAuthorities().add(authority);
  28. } else {
  29. logger.warn("person authorities is empty, personId is [{}]", username);
  30. }
  31. }
  32. //加载用户权限
  33. userDetails.getAuthorities().addAll(authorities);
  34. ///这个就是权限系统最后的用户信息
  35. return userDetails;
  36. }
  37. private boolean isCommonUserRequest() {
  38. // TODO Auto-generated method stub
  39. return true;
  40. }
  41. public AdminService getAdminService() {
  42. return adminService;
  43. }
  44. public void setAdminService(AdminService adminService) {
  45. this.adminService = adminService;
  46. }
  47. }

  1. public class AdminAuthenticationToken extends AbstractAuthenticationToken {
  2. /**
  3. *
  4. */
  5. private static final long serialVersionUID = 5976309306377973996L;
  6. private final Object principal;
  7. private Object credentials;
  8. public AdminAuthenticationToken(Object principal, Object credentials) {
  9. super(null);
  10. this.principal = principal;
  11. this.credentials = credentials;
  12. setAuthenticated(false);
  13. }
  14. c AdminAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
  15. super(authorities);
  16. this.principal = principal;
  17. this.credentials = credentials;
  18. super.setAuthenticated(true); //注意,这里设置为true了! must use super, as we override
  19. }
  20. public Object getCredentials() {
  21. return this.credentials;
  22. }
  23. public Object getPrincipal() {
  24. return this.principal;
  25. }
  26. public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
  27. if (isAuthenticated) {
  28. throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
  29. }
  30. super.setAuthenticated(false);
  31. }
  32. @Override
  33. public void eraseCredentials() {
  34. super.eraseCredentials();
  35. credentials = null;
  36. }
  37. }

到这里基本就完成了spring security的设置,

4)最后就是jsp页面部分,页面主要是表单的action地址必须为:path+/j_spring_security_check,其中path是项目路径,然后登录的时候就可以使用security模块了!

至于文章中可能涉及到的一些bean或者service,这里就不贴代码了,毕竟项目较为完善,涉及东西太多,另外每个项目业务逻辑也不一样。

在整个框架整合过程中,遇到了很多奇怪的问题,很多是因为版本不一致引起的,建议用maven进行jar等依赖包的统一管理。

相关推荐

全球最大的H5网站模板库(h5页面模板下载)

当今社会,互联网迅猛发展,在网络营销中,客户往往通过企业的网站建设留下对该企业的第一印象,一个优秀的企业网站已成为企业发展的重要纽带,嗨创H5,拥有国内外一流的技术团队,潜心专研网站建设6年,是全球最...

wordpress集团公司网站模板:XSgr(wordpress建站公司)

小兽wordpress推出一款高端集团公司主题,打造高品质官网。高端是一种态度和坚持,因为我坚信贴合产品及品牌理念的高端深度定制才能最大化地呈现企业的务实严谨与产品的专业品质相比,某种程度上讲–...

私心推荐,小编酷爱的五款高逼格网站模板

建站宝盒的网站模板上千套之多,各有各的风格色彩,但是,弱水三千,小编我却只取一瓢饮,在这上千套模板之中,小编酷爱的网站模板有五套,让小编私心推荐一下吧!1、茶叶贸易公司网站模板小编对这款网站模板可是一...

「书讯」政府网站用户行为研究与应用

《政府网站用户行为研究与应用》作者:刘合翔著出版日期:2018年6月开本:16开出版社:经济管理出版社小编推荐《政府网站用户行为研究与应用》的主题是关于政府网站用户行为的特征规律及其在政府网站优...

免费服务器-搭建模板网站的操作流程(图文版)

之前发文《创业者的官网:如何搭建免费云服务器及操作面板(图文版)》,因为做了视频才发现,创业者对视频的需求,远远低于对图文解说的需求。因此,补充图文教程,不清楚的看官们,可以直接看视频版本进行细部学...

快收藏这些高逼格H5网站模板吧,不绕弯子直接下载

上面这些响应式H5网站是不是很炫酷,比起那些“在线一键生成”是不是好太多了?关键是,那些一键制作都不会开放源码给你,自定义性也很局限。不过说到底还是难看。今天笔者推荐大家一个模板网站,全都是高质量的响...

如何开发网站建设管理系统模板(如何开发网站建设管理系统模板图片)

根据用户网站需求文档设计美工图,并设计数据库结构,让网站开发人员可以更多地关注前台美工,先对照美工图,编写静态HTML页面,按网站建设管理系统模板语法,修改编写好的静态HTML页面,运行。不再需要对...

C语言的数据类型介绍(c语言的数据类型介绍是什么)

在计算机系统中,数据是放在内存中的,数字、文字、符号、图形、音频、视频等数据都是以二进制形式存储在内存中的,它们并没有本质上的区别,那么0001000该理解为数字8呢,还是图像中某个像素的颜色...

C 语言格式化输出函数中常用的格式符号

在之前介绍输入输出函数的文章中,有提到格式化输入输出函数都有包含一种特殊的符号——格式符号。那篇文章中关于格式符号也只是一笔带过,没有进行深入挖掘。本篇文章主要对输出函数(printf)中的一些常用格...

C#中的类型转换(c#数据转换类)

计算机存储的基本单位:字节我们知道一个字节(Byte)有8个比特(bit)构成,比特是存储的最小单位,表示0和1,但为什么计算机存储的基本单位是字节,而不是比特呢?假设我们要存储数字3(二进制:11...

Java8中String内存空间占用分析(电脑里下载的文件怎样删除才不会占用内存空间)

1.前言分析之前,简单回顾一下对象的内存分布。在HotSpot虚拟机中,对象在堆内存中的存储布局可以划分为三部分:对象头、实例数据和对齐填充。对象头包含两部分内容:MarkWord和类型指针。实例数据...

「每日C语言」数据类型大小和取值范围

对于c语言来说,数据类型是一个很重要的概念和知识点,它涉及到的是内存的空间,这在和硬件交互的时候是非常重要的。K&R给出了7个数据类型相关的关键字,分别是:int、long、short、uns...

【c语言学习笔记】数据类型(c语言里面的数据类型)

c语言学习笔记,欢迎大家能在评论区提出我学习错误的地方方便我进行改正~在计算机中,计算机用二进制来储存数据,在c语言中有许多的数据类型用来存储数据,当然不同的数据类型所用的内存占用也不一样,下面就来用...

关于MySQL varchar类型最大值,原来一直都理解错了

我是架构精进之路,点击上方“关注”,坚持每天为你分享技术干货,私信我回复“01”,送你一份程序员成长进阶大礼包。写在前面关于MySQLvarchar字段类型的最大值计算,也许我们一直都理解错误了,...

C语言数据类型的转换(c语言数据类型的转换方式)

类型转换在C语言程序中,经常需要对不同类型的数据进行运算,为了解决数据类型不一致的问题,需要对数据的类型进行转换。例如一个浮点数和一个整数相加,必须先将两个数转换成同一类型。C语言程序中的类型...

取消回复欢迎 发表评论: