【Spring Security实战系列】Spring Security实战(四)

版权声明:【本文为博主原创文章或收集整理,未经博主允许不得转载】 https://blog.csdn.net/zsq520520/article/details/77881307
 在前面三个实战中,登陆页面都是用的Spring Security自己提供的,这明显不符合实际开发场景,同时也没有退出和注销按钮,因此在每次测试的时候都要通过关闭浏览器来注销达到清除session的效果。
一 自定义页面

login.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="th" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html lang="en">

<html>
<head>
    <%--<meta name="_csrf" content="${_csrf.token}"/>
    <meta name="_csrf_header" content="${_csrf.headerName}"/>
    <meta name="_csrf_parameter" content="_csrf" />--%>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>登陆</title>
</head>
<body>
    <div class="error ${param.error == true ? '' : 'hide'}">
        登陆失败<br>
        ${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}
    </div>
    <%--
        特别要注意的是form表单的action是提交登陆信息的地址,这是security内部定义好的,
        同时自定义form时,要把form的action设置为/j_spring_security_check。
        注意这里要使用绝对路径,避免登陆页面存放的页面可能带来的问题。
    --%>
    <form method="post" action="${pageContext.request.contextPath}/j_spring_security_check"
          style="width:260px; text-align: center">
    <fieldset>
            <legend>新页面-登陆</legend>
            <%--j_username,输入登陆名的参数名称,j_password,输入密码的参数名称,这两个正常情况下也不会修改--%>
            用户: <input type="text" name="j_username" style="width: 150px;"
                       value="${sessionScope['SPRING_SECURITY_LAST_USERNAME']}" /><br />
            密码: <input type="password" name="j_password" style="width: 150px;" /><br />
            <%--
                _spring_security_remember_me,选择是否允许自动登录的参数名称。
                可以直接把这个参数设置为一个checkbox,无需设置value,Spring Security会自行判断它是否被选中,
                这也是security内部提供的,只需要配置,不需要自己实现。
            --%>

            <input type="checkbox" name="_spring_security_remember_me" />两周之内不必登陆<br />
            <input type="submit" value="登陆" />
            <input type="reset" value="重置" />
        </fieldset>
    </form>
</body>
</html>

说明:
1、特别要注意的是form表单的action是提交登陆信息的地址,这是security内部定义好的,同时自定义form时,要把form的action设置为/j_spring_security_check。注意这里要使用绝对路径,避免登陆页面存放的页面可能带来的问题。
2、j_username,输入登陆名的参数名称,j_password,输入密码的参数名称,这两个正常情况下也不会修改。
3、_spring_security_remember_me,选择是否允许自动登录的参数名称。可以直接把这个参数设置为一个checkbox,无需设置value,Spring Security会自行判断它是否 被选中,这也是security内部提供的,只需要配置,不需要自己实现。
二 配置制定的页面
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:sec="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/context
                        http://www.springframework.org/schema/context/spring-context-3.1.xsd
                        http://www.springframework.org/schema/tx
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security.xsd">


    <!-- 配置不过滤的资源(静态资源及登录相关).是忽略拦截某些资源的意思,主要是针对静态资源 -->
    <http pattern="/**/*.css" security="none"></http>
    <http pattern="/**/*.jpg" security="none"></http>
    <http pattern="/**/*.jpeg" security="none"></http>
    <http pattern="/**/*.gif" security="none"></http>
    <http pattern="/**/*.png" security="none"></http>
    <http pattern="/js/*.js" security="none"></http>

    <http pattern="/login.jsp" security="none"></http>
    <http pattern="/getCode" security="none" /><!-- 不过滤验证码 -->
    <http pattern="/test/**" security="none"></http><!-- 不过滤测试内容 -->

    <http auto-config="false">
        <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <!-- 表示访问app.jsp时,需要ROLE_SERVICE权限 -->
        <intercept-url pattern="/adminPage.jsp" access="hasRole('ROLE_ADMIN')"></intercept-url>
        <!--表示访问任何资源都需要ROLE_ADMIN权限。-->
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')"></intercept-url>
        <!--
            登陆页面肯定是不能拦截的,任何人都应该可以访问,
            <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />配置表示允许匿名用户访问,
            就是不用身份都可以访问;
            还有另一种配置方式:<http pattern="/login.jsp" security="none"></http>,这种配置达到的目的都是一样的。
        -->


        <!--
            form-login这个标签是配置登陆页面的,其中的属性login-page是配置登陆页面的,
            default-target-url配置登陆成功后跳转到的页面,
            authentication-failure-url配置认证失败后的跳转页面。

            form-login标签中还有一个特别要注意的属性use-expressions,如果设置为true,
            这配置access就要做相应的改变,否则项目启动的时候会报错。
            如果use-expressns="true"时,则表示改为 SpEL 表达式。 SpEL 允许使用特定的访问控制规则表达式语言。
            与简单的字符串如 ROLE_USER 不同,配置文件可以指明表达式语言触发方法调用、引用系统属性、计算机值等等。
            如 :<intercept-url pattern="/login.jsp" access="permitAll" />
        -->
        <form-login login-page="/login.jsp" default-target-url="/index.jsp"
                    always-use-default-target="false"
                    authentication-failure-url="/login.jsp?error=true"></form-login>

        <!--
            logout这个标签用来配置退出或者注销,其中的属性invalidate-session,
            配置否是要清除session,logout-success-url配置注销成功后的跳转页面,
            logout-url提交退出或者注销的地址,因此我们在配置退出或者注销的时候,
            只需要将url设置为/j_spring_security_logout即可,这个地址也是security内部实现了的。
        -->
        <logout invalidate-session="true" logout-success-url="/login.jsp"
                logout-url="/j_spring_security_logout"></logout>

        <!--将CSRF保护功能禁用,设置为true即为启用-->
        <!-- 必须添加此段声明,禁用CSRF功能 -->
        <!-- <csrf disabled="false"/>-->

        <!--<csrf request-matcher-ref="csrfSecurityRequestMatcher"></csrf>-->


        <!-- max-sessions只容许一个账号登录,error-if-maximum-exceeded 后面账号登录后踢出前一个账号,
              expired-url session过期跳转界面
              如果concurrency-control标签配置了error-if-maximum-exceeded="true",max-sessions="1",
              那么第二次登录时,是登录不了的。如果error-if-maximum-exceeded="false",
              那么第二次是能够登录到系统的,但是第一个登录的账号再次发起请求时,会跳转到expired-url配置的url中-->
        <session-management session-authentication-error-url="/login.jsp">
            <concurrency-control max-sessions="1" error-if-maximum-exceeded="false"
                                 expired-url="/login.jsp" session-registry-ref="sessionRegistry" />
        </session-management>

        <expression-handler ref="webexpressionHandler" ></expression-handler>

    </http>

    <!--这里添加了一个属性execludeUrls,允许人为排除哪些url。
    这里约定所有/rest/开头的都是Rest服务地址,上面的配置就把/rest/排除在csrf验证的范围之外了.
    源码可以发现,POST方法被排除在外了,也就是说只有GET|HEAD|TRACE|OPTIONS这4类方法会被放行,
    其它Method的http请求,都要验证_csrf的token是否正确,
    而通常post方式调用rest服务时,又没有_csrf的token,所以校验失败。
    解决方法:自己弄一个Matcher-->
    <!--<beans:bean id="csrfSecurityRequestMatcher" class="cn.quan.ssm.sec.CsrfSecurityRequestMatcher">
        <beans:property name="execludeUrls">
            <beans:list>
                <beans:value>/rest/</beans:value>
            </beans:list>
        </beans:property>
    </beans:bean>-->

    <beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl"/>
    <!--配置web端使用权限控制-->
    <beans:bean id="webexpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>

    <!-- 导入数据源 -->
    <beans:import resource="applicationContext-dataSource.xml"></beans:import>

    <!-- 默认数据库对用户进行存储 Spring Security默认情况下需要两张表,用户表和权限表。-->
    <authentication-manager>
        <authentication-provider>
           <!-- <user-service>
                <user name="admin" password="123" authorities="ROLE_USER, ROLE_ADMIN" />
                <user name="user" password="123" authorities="ROLE_USER" />
            </user-service>-->
            <jdbc-user-service data-source-ref="mysqlDataSource"
                  users-by-username-query="select username,`password`,`status` as enabled from `user` where username = ?"
                  authorities-by-username-query="select `user`.username,role.`name` from `user`,role,user_role where `user`.id=user_role.user_id and user_role.role_id=role.id and `user`.username = ?" />

        </authentication-provider>
    </authentication-manager>
</beans:beans>
说明:
1、form-login这个标签是配置登陆页面的,其中的属性login-page是配置登陆页面的,default-target-url配置登陆成功后跳转到的页面,authentication-failure-url配置认证失败后的跳转页面。
2、在上面的配置中,登陆页面肯定是不能拦截的,任何人都应该可以访问,<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />配置表示允许匿名用户访问,就是不用身份都可以访问;还有另一种配置方式:<http pattern="/login.jsp" security="none"></http>,这种配置达到的目的都是一样的。
3、logout这个标签用来配置退出或者注销,其中的属性invalidate-session,配置否是要清除session,logout-success-url配置注销成功后的跳转页面,logout-url提交退出或者注销的地址,因此我们在配置退出或者注销的时候,只需要将url设置为/j_spring_security_logout即可,这个地址也是security内部实现了的。
4、form-login标签中还有一个特别要注意的属性use-expressions,如果设置为true,这配置access就要做相应的改变,否则项目启动的时候会报错,错误如下:


如果use-expressns="true"时,则表示改为 SpEL 表达式。 SpEL 允许使用特定的访问控制规则表达式语言。与简单的字符串如 ROLE_USER 不同,配置文件可以指明表达式语言触发方法调用、引用系统属性、计算机值等等。http标签中的配置改为如下:
    <http auto-config="false" use-expressions="true">
        <intercept-url pattern="/login.jsp" access="permitAll" />
        <!-- 表示访问app.jsp时,需要ROLE_SERVICE权限 -->
        <intercept-url pattern="/adminPage.jsp" access="hasRole('ROLE_ADMIN')"></intercept-url>
        <!--表示访问任何资源都需要ROLE_ADMIN权限。-->
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')"></intercept-url>
        <!--
            登陆页面肯定是不能拦截的,任何人都应该可以访问,
            <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />配置表示允许匿名用户访问,
            就是不用身份都可以访问;
            还有另一种配置方式:<http pattern="/login.jsp" security="none"></http>,这种配置达到的目的都是一样的。
        -->


        <!--
            form-login这个标签是配置登陆页面的,其中的属性login-page是配置登陆页面的,
            default-target-url配置登陆成功后跳转到的页面,
            authentication-failure-url配置认证失败后的跳转页面。

            form-login标签中还有一个特别要注意的属性use-expressions,如果设置为true,
            这配置access就要做相应的改变,否则项目启动的时候会报错。
            如果use-expressns="true"时,则表示改为 SpEL 表达式。 SpEL 允许使用特定的访问控制规则表达式语言。
            与简单的字符串如 ROLE_USER 不同,配置文件可以指明表达式语言触发方法调用、引用系统属性、计算机值等等。
            如 :<intercept-url pattern="/login.jsp" access="permitAll" />
        -->
        <form-login login-page="/login.jsp" default-target-url="/index.jsp"
                    always-use-default-target="false"
                    authentication-failure-url="/login.jsp?error=true"></form-login>

        <!--
            logout这个标签用来配置退出或者注销,其中的属性invalidate-session,
            配置否是要清除session,logout-success-url配置注销成功后的跳转页面,
            logout-url提交退出或者注销的地址,因此我们在配置退出或者注销的时候,
            只需要将url设置为/j_spring_security_logout即可,这个地址也是security内部实现了的。
        -->
        <logout invalidate-session="true" logout-success-url="/login.jsp"
                logout-url="/j_spring_security_logout"></logout>

        <!--将CSRF保护功能禁用,设置为true即为启用-->
        <!-- 必须添加此段声明,禁用CSRF功能 -->
        <!-- <csrf disabled="false"/>-->

        <!--<csrf request-matcher-ref="csrfSecurityRequestMatcher"></csrf>-->


        <!-- max-sessions只容许一个账号登录,error-if-maximum-exceeded 后面账号登录后踢出前一个账号,
              expired-url session过期跳转界面
              如果concurrency-control标签配置了error-if-maximum-exceeded="true",max-sessions="1",
              那么第二次登录时,是登录不了的。如果error-if-maximum-exceeded="false",
              那么第二次是能够登录到系统的,但是第一个登录的账号再次发起请求时,会跳转到expired-url配置的url中-->
        <session-management session-authentication-error-url="/login.jsp">
            <concurrency-control max-sessions="1" error-if-maximum-exceeded="false"
                                 expired-url="/login.jsp" session-registry-ref="sessionRegistry" />
        </session-management>

        <expression-handler ref="webexpressionHandler" ></expression-handler>

    </http>
配置文件中的其他配置(如applicationContext.xml、applicationContext-dataSource.xml、logback.xml、datasource.properties)在前面几篇博客中都有详细的讲解,这里就不赘述了。
三 其他文件

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<!DOCTYPE html>
<html lang="en">

<html>
<body>
<h2>this is a user page </h2>
<a href="${pageContext.request.contextPath}/j_spring_security_logout">退出登陆</a>
</body>
</html>

这里定义了两个页面,index.jsp用户和管理员都可以访问,adminPage.jsp只有管理员可以访问,同时两个页面都有注销按钮,注销按钮提交的地址也就是上面配置文件中的地址/j_spring_security_logout。

pom.xml和前面的一样,这里就不贴了。
四 结果


当输入普通用户的用户名和密码,同时勾选2周不用登陆后,因为adminPage.jsp页面要有管理员权限才能访问,所以普通用户访问失败,index.jsp页面就可以访问;这时关闭页面后,再次访问资源,因为勾选了2周不用登陆,所以可以成功访问;但是当点击退出登录后,再次访问是就会跳转到登陆页面,要求登陆才能访问。

当输入管理员名和密码,同时勾选2周不用登陆,验证成功后,跳转到index.jsp,同时adminPage.jsp也可以访问,这时把一个页面关闭再重新访问资源时,因为勾选2周不用登陆,所以可以成功访问;然后注销,这是再访问资源时,就会跳转到登陆页面,要求登陆才能访问。

猜你喜欢

转载自blog.csdn.net/zsq520520/article/details/77881307