美文网首页我爱编程
Spring Security 自定义登陆表单

Spring Security 自定义登陆表单

作者: wanggs | 来源:发表于2018-05-25 00:21 被阅读0次
    虽然 Spring Security 提供了默认的登录表单,实际项目里肯定是不可以直接使用的,当然 Spring Security 也提供了自定义登录表单的功能。

    spring-security.xml

    image.png
        
    <?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"
            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">
        <http security="none" pattern="/static/**"/>
        <http auto-config="true">
            <intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
            <intercept-url pattern="/login" access="permitAll"/>
            <form-login login-page="/login"
                        login-processing-url="/login"
                        default-target-url  ="/hello"
                        authentication-failure-url="/login?error"
                        username-parameter="username"
                        password-parameter="password"/>
            <access-denied-handler error-page="/deny" />
            <logout logout-url="/logout" logout-success-url="/login?logout" />
            <csrf disabled="true"/>
        </http>
        <authentication-manager>
            <authentication-provider>
                <user-service>
                    <user name="admin" password="{noop}Passw0rd" authorities="ROLE_ADMIN"/>
                    <user name="alice" password="{noop}Passw0rd" authorities="ROLE_USER"/>
                </user-service>
            </authentication-provider>
        </authentication-manager>
    </beans:beans>
    

    Spring Security 允许多个 <http> 存在:

    上面的配置 <intercept-url pattern="/login" access="permitAll"/>,导致访问 /login 页面时也要经过很多 filter,例如 UsernamePasswordAuthenticationFilter, SecurityContextPersistenceFilter, SessionRepositoryFilter 等,其实不需要登陆就能访问的页面,例如登陆页面, js, css, png 等是不需要经过这些 filter 的,则可以配置 http 的 security 为 none,这样就能提高程序的效率:

    <http security="none" pattern="/page/login"/>
    <http security="none" pattern="/static/**"/>
    
    

    LoginController

    前一节中,注销是在 LoginController 中使用函数 logoutPage() 来处理的,这一节注销直接在 spring-security.xml 里配置 <logout> 来实现的,把 logoutPage() 函数从 LoginController 里删除

        
    package com.xtuer.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.*;
    @Controller
    public class LoginController {
        @GetMapping("/login")
        public String loginPage(@RequestParam(value="error", required=false) String error,
                                @RequestParam(value="logout", required=false) String logout,
                                ModelMap model) {
            if (error != null) {
                model.put("error", "Username or password is not correct");
            } else if (logout != null) {
                model.put("logout", "Logout successful");
            }
            return "login.html";
        }
        @RequestMapping("/deny")
        @ResponseBody
        public String denyPage() {
            return "You have no permission to access the page";
        }
    }
    

    登陆表单 login.html

    <html>
    <head>
        <title>Login Page</title>
    </head>
    <body>
        <span th:text="${error}" th:if="${error} != null"></span>
        <span th:text="${logout}" th:if="${logout} != null"></span>
        <form name="loginForm" action="/login" method="POST">
            Username: <input type="text" name="username" /><br>
            Password: <input type="password" name="password" /><br>
            <input name="submit" type="submit" value="登陆" />
        </form>
    </body>
    </html>
    

    测试

    访问 http://localhost:8080/hello
    访问 http://localhost:8080/admin
    输入错误的用户名或密码,观察登陆失败的页面
    输入正确的用户名和密码 admin/Passw0rd,继续登陆
    访问 http://localhost:8080/logout,观察注销成功的页面
    访问 http://localhost:8080/admin
    输入正确的用户名和密码 alice/Passw0rd,继续登陆
    提示权限不够
    

    自定义登陆成功的 handler

    设置 authentication-success-handler-ref 为自定义登陆成功的 handler,就可以使用同一个登录表单,登录成功后根据用户的角色或者权限重定向到不同的页面:

        
    <beans:bean id="loginSuccessHandler" class="com.xtuer.security.LoginSuccessHandler"/>
    <http security="none" pattern="/static/**"/>
    <http auto-config="true">
        <intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
        <intercept-url pattern="/login" access="permitAll"/>
        <form-login login-page="/login"
                    login-processing-url="/login"
                    default-target-url  ="/"
                    authentication-success-handler-ref="loginSuccessHandler"
                    authentication-failure-url="/login?error"
                    username-parameter="username"
                    password-parameter="password"/>
        <access-denied-handler error-page="/deny" />
        <logout logout-url="/logout" logout-success-url="/login?logout" />
        <csrf disabled="true"/> <!-- 去掉这一行不行 -->
    </http>
    

    注意:自定义登录成功的 handler 后 default-target-url 不再起作用。

        
    package com.xtuer.security;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
    import org.springframework.security.web.savedrequest.SavedRequest;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    public class LoginSuccessHandler implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request,
                                            HttpServletResponse response,
                                            Authentication authentication) throws IOException, ServletException {
            // 用户登录成功后访问不同的页面:
            // 1. 获取用户登录前访问的 URL
            // 2. 如果 url 不为空,访问登录前的页面
            // 3. 如果 url 为空,登录后根据用户的角色访问对应的页面
            SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(request, response);
            String redirectUrl = (savedRequest == null) ? "" : savedRequest.getRedirectUrl();
            if (!redirectUrl.isEmpty()) {
                response.sendRedirect(redirectUrl);
                return;
            }
            // 获取登录用户信息
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            for (GrantedAuthority authority : auth.getAuthorities()) {
                String role = authority.getAuthority(); // 用户的权限
                // 不同的用户访问不同的页面
                if ("ROLE_ADMIN".equals(role)) {
                    redirectUrl = "/admin";
                } else {
                    redirectUrl = "/hello";
                }
            }
            response.sendRedirect(redirectUrl);
        }
    }
    

    相关文章

      网友评论

        本文标题:Spring Security 自定义登陆表单

        本文链接:https://www.haomeiwen.com/subject/glnejftx.html