JavaWeb进阶修炼手册37---spring-security(三)ssm整合spring-security

1. 今日内容

2. ssm整合spring-security

ssm整合spring-security后,整个请求处理的流程图:
在这里插入图片描述

* 流程详解:
	1. 前端发送的所以请求都应该被`spring-security`拦截

	2. `spring-security`拦截后判断该请求是否是登入请求,即否是`login-processing-url`指定的请求
		* 若不是,则一律响应给用户login-page指定的页面
		* 若是,则spring-security内部判断该用户是否有权限
			* 若没有,则响应给用户authentication-failure-url指定的页面
			* 若有,则进行第3步

	3. 跳转到default-target-url指定的页面,跳转成功后发出authentication-success-forward-url指定的请求给springmvc处理
		同时给用户一个cookie,下次用户再访问资源时,spring-security根据cookie验证,没有cookie再跳转到login-page指定的登入页面

整合步骤:

配置ssm的web.xml文件,在该文件中加载spring-security.xml文件:

<!-- 配置加载类路径的配置文件 -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
</context-param>

再设置过滤器,哪些请求给spring-security先处理:

<!-- 拦截哪些请求给spring-security处理验证 -->
<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>

这样就整合好了,很简单,但是你可能遇到一些问题,后面介绍。

我的ssm的web.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app.xsd"
         version="3.1">

    <!-- 配置加载类路径的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
    </context-param>

    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 解决中文乱码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</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>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 拦截哪些请求给spring-security处理验证 -->
    <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>

    <!-- 前端控制器(加载classpath:springmvc.xml 服务器启动创建servlet) -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

3. 整合过程中遇到的问题

3.1 404错误

3.1.1 第一种可能

如果你设置的登入页面是.html页面,则会出现404的问题。因为spring-security要操作页面,都是请求springmvc得到的。spring-security要跳转到login-page="/ogin.html",会给springmvc发送/login的请求,而html是静态资源,如果你不在spring-mvc.xml配置文件中设置允许该静态资源允许被访问,则会找不到资源。

所以,在spring-mvc.xml配置文件中编写一下代码:

<mvc:resources location="/login.html" mapping="/login.html"/>

然后就可以访问资源了。不过个人建议用jsp比较好。

3.1.2 第二种可能

没有在spring-security.xml中设置csrf:<security:csrf disabled="true"/>,其位置如下:

<security:http auto-config="true" use-expressions="false">
    <!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER"/>
    <security:form-login
            login-page="/pages/login.jsp"
            login-processing-url="/login"
            username-parameter="username"
            password-parameter="password"
            authentication-failure-url="/pages/failure.jsp"
            default-target-url="/pages/success.jsp"
            always-use-default-target="true"
            />
    <security:logout invalidate-session="true" logout-url="/logout"
                     logout-success-url="/login.jsp"/>

    <!-- 关闭CSRF,默认是开启的 -->
    <security:csrf disabled="true"/>
</security:http>

3.2 页面上显示错误“重定向请求过多”

你用spring-security拦截了所有的请求,但是,请求登入界面的请求不应该被拦截,不然就陷入了死循环,一直重定向,造成此错误。
应该设置:<security:http security="none" pattern="/pages/login.jsp"/>,表示不过滤某些请求,其位置如下:

<!-- 配置不过滤的资源(静态资源及登录相关) -->
<security:http security="none" pattern="/pages/login.jsp"/>

<security:http auto-config="true" use-expressions="false">
    <!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 -->
    <security:intercept-url pattern="/**" access="ROLE_USER"/>
    <security:form-login
            login-page="/pages/login.jsp"
            login-processing-url="/login"
            username-parameter="username"
            password-parameter="password"
            authentication-failure-url="/pages/failure.jsp"
            default-target-url="/pages/success.jsp"
            always-use-default-target="true"
            />
    <security:logout invalidate-session="true" logout-url="/logout"
                     logout-success-url="/login.jsp"/>

    <!-- 关闭CSRF,默认是开启的 -->
    <security:csrf disabled="true"/>
</security:http>

3.3 无论登入成功还是失败一直重定向在登入界面

3.3.1 第一个可能

出现这个原因,你应该和我一样用的是jsp文件,我都网上翻了各种资料,找了好几个小时,没找到。第二天自己无意间把错误找到了:

在登入的login.jsp页面中,登入的请求必须要绝对请求路径,不能用相对请求路径:

必须这样:
<form action="${pageContext.request.contextPath}/login" method="post">

不能这样:
<form action="login" method="post">

3.3.2 第二个可能

登陆界面的请求与login-processing-url请求的名字不一样:

<form action="${pageContext.request.contextPath}/login" method="post">

login-processing-url="/login"

这里 action="${pageContext.request.contextPath}/XXX" 和 login-processing-url="/XXX"
这里的XXX必须一致

这样基本上没有问题了。

4. 最后附上我的ssm整合spring-security项目

项目结构:
在这里插入图片描述
效果:
在这里插入图片描述

4.1 web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app.xsd"
         version="3.1">

    <!-- 配置加载类路径的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml,classpath*:spring-security.xml</param-value>
    </context-param>

    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 解决中文乱码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</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>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <!-- 拦截哪些请求给spring-security处理验证 -->
    <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>

    <!-- 前端控制器(加载classpath:springmvc.xml 服务器启动创建servlet) -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化参数,创建完DispatcherServlet对象,加载springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 服务器启动的时候,让DispatcherServlet对象创建 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

4.2 spring-security.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security.xsd">

    <!-- 配置不过滤的资源(静态资源及登录相关) -->
    <security:http security="none" pattern="/login.do"/>
    <security:http security="none" pattern="/login.do?error=a"/>

    <security:http auto-config="true" use-expressions="false">
        <!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 -->
        <security:intercept-url pattern="/**" access="ROLE_USER"/>

        <!-- 自定义登陆页面
            login-page 登陆页面
            login-processing-url : 发送登陆请求数据的url
            authentication-failure-url 用户权限校验失败后后才会跳转到这个页面,
            default-target-url 登陆成功后跳转的页面。
            always-use-default-target: true 适用于后台管理系统,防止访问历史记录
                                       false 适用于前台页面,提升用户体验
        -->
        <security:form-login
                login-page="/login.do"
                login-processing-url="/login"
                username-parameter="username"
                password-parameter="password"
                authentication-failure-url="/login.do?error=a"
                default-target-url="/pages/success.jsp"
                always-use-default-target="true"
                />

        <!-- 登出:
            invalidate-session 是否删除session
            logout-url:登出处理链接
            logout-successurl:登出成功页面
            注:登出操作 只需要链接到 logout即可登出当前用户
        -->
        <security:logout
                invalidate-session="true"
                logout-url="/logout"
                logout-success-url="/login.do?logout"/>

        <!-- 关闭CSRF,默认是开启的 -->
        <security:csrf disabled="true"/>
    </security:http>

    <!-- 在内存中构造用户们 -->
    <security:authentication-manager>
        <security:authentication-provider>
            <security:user-service>
                <security:user name="user" password="{noop}user" authorities="ROLE_USER"/>
                <security:user name="admin" password="{noop}admin" authorities="ROLE_ADMIN"/>
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans>

4.3 spring-mvc.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
               http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/mvc
               http://www.springframework.org/schema/mvc/spring-mvc.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描controller的注解,别的不扫描 -->
    <context:component-scan base-package="cn.wanghao.springSecurity.controller"></context:component-scan>

    <!-- 开启对SpringMVC注解的支持 -->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 配置视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- JSP文件所在的目录 -->
        <property name="prefix" value="/pages/"></property>
        <!-- 文件的后缀名 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 设置静态资源不过滤 -->
    <!--<mvc:resources location="/css/" mapping="/css/**"/>-->
    <!--<mvc:resources location="/images/" mapping="/images/**"/>-->
    <!--<mvc:resources location="/js/" mapping="/js/**"/>-->

</beans>

4.4 login.jsp文件

<%--
  Created by IntelliJ IDEA.
  User: WangHao
  Date: 2020/4/4
  Time: 16:24
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
    <label>用户名:</label> <input type="text" name="username"> <br>
    <label>密码:</label>  <input type="password" name="password"> <br>
    <input type="submit" value="提交"> <input type="reset" value="重置"/>
    <span>
        <c:if test="${not empty error}">
            <div>${error}</div>
        </c:if>
		<c:if test="${not empty logout}">
            <div>${logout}</div>
        </c:if>
    </span>
</form>
</body>
</html>

4.5 success.jsp

<%--
  Created by IntelliJ IDEA.
  User: WangHao
  Date: 2020/4/4
  Time: 14:18
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    恭喜您,自定义登入设置成功!<br>
    <a href="${pageContext.request.contextPath}/logout">注销</a>
</body>
</html>

4.6 LoginController.java

package cn.wanghao.springSecurity.controller;

import cn.wanghao.springSecurity.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {

    /**
     * 处理登入请求
     *  1. 不带error、logout参数时,跳转到登入页面
     *  2. 带error时,表示登入失败,需要返回给前端登陆失败的信息
     *  3. 带logout时,表示注销,需要返回给前端注销成功的信息
     * @param error
     * @param logout
     * @return 跳转登陆页面
     */
    @RequestMapping("/login.do")
    public ModelAndView login(
            @RequestParam(value = "error", required = false)String error,
            @RequestParam(value = "logout", required = false)String logout) {
        ModelAndView model = new ModelAndView();
        if (error != null) {
            model.addObject("error", "登入失败!");
        }

        if (logout != null) {
            model.addObject("logout", "注销成功!");
        }
        model.setViewName("login");
        return model;
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章