Spring Security认证配置(二)

学习本章之前,可以先了解下上篇Spring Security基本配置

本篇想要达到这样几个目的:

1、访问调用者服务时,如果是html请求,则跳转到登录页,否则返回401状态码和错误信息

2、调用方可自定义登录页面。如果没有配置,则使用认证中心的标准登录页

对于第一点,可以画个图:

接下来看具体的配置:

 

spring-security-browser

先建一个认证中需要用到的常量类:

/**
 * Security相关常量类
 */
public class SecurityConst {

    /** 默认登录页url */
    public static final String AUTH_REQUIRE = "/authentication/require";
    
    /** 登录页请求url */
    public static final String AUTH_FORM = "/authentication/form";

}

在src/main/resources下再建立一个resources目录,用来放静态文件。然后在resources下建立login.html页面:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
    <h2>标准登录页面</h2>
    <h3>表单登录</h3>
    <form action="/authentication/form" method="post">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码:</td>
                <td><input type="password" name="password"></td>
            </tr>
            <tr>
                <td colspan="2">
                    <button type="submit">登录</button>
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

建立配置类(方便服务调用者自定义配置):

/**
 * security相关配置(不能取名SecurityProperties,因为security中也有这个类名,不然会报错)
 */
@Getter
@Setter
@ConfigurationProperties(prefix = "security")
public class SecurityProperty {

    private BrowserProperty browser = new BrowserProperty();

}
/**
 * security-浏览器相关配置
 */
@Getter
@Setter
public class BrowserProperty {

    private String loginPage = "/login.html"; // 登录页  

}

修改下SecurityConfig类中的配置:

   @Autowired
    private SecurityProperty securityProperty;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // formLogin()是默认的登录表单页,如果不配置 loginPage(url),则使用 spring security
        // 默认的登录页,如果配置了 loginPage()则使用自定义的登录页
        http.formLogin() // 表单登录
            .loginPage(SecurityConst.AUTH_REQUIRE) 
            .loginProcessingUrl(SecurityConst.AUTH_FORM) // 登录请求拦截的url,也就是form表单提交时指定的action
            .and()
            .authorizeRequests() // 对请求授权
            .antMatchers(SecurityConst.AUTH_REQUIRE, securityProperty.getBrowser().getLoginPage()).permitAll() // 允许所有人访问login.html和自定义的登录页
            .anyRequest() // 任何请求
            .authenticated()// 需要身份认证
            .and()
            .csrf().disable() // 关闭跨站伪造
        ;
    }

配置代码分析:

从上面可以看出,当第一次请求时(如http://localhost:18081/user),会先跳转到loginPage配置的路径里,即/authentication/require

/authentication/require/login.html这两个请求不需要认证授权(第二个url是登录页,可以在调用方自定义配置的)

接下来,建立一个Controller,用来处理/authentication/require请求:

@Slf4j
@RestController
public class SecurityController {

    private RequestCache requestCache = new HttpSessionRequestCache(); // 请求缓存

    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();// 页面跳转

    @Autowired
    private SecurityProperty securityProperty;

    /**
     * 当需要身份认证时,跳转到这里 (如果是html请求,则跳转到登录页,否则返回401状态码和错误信息)
     */
    @SneakyThrows
    @RequestMapping(SecurityConst.AUTH_REQUIRE)
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED) // 未授权状态码401
    public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) {
        // 从session中取之前缓存的请求
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest != null) {
            String targetUrl = savedRequest.getRedirectUrl();
            log.info("引发跳转的请求是:" + targetUrl);
            if (StringUtils.endsWithIgnoreCase(targetUrl, ".html")) {
                // 跳转到指定的页面
                redirectStrategy.sendRedirect(request, response, securityProperty.getBrowser().getLoginPage());
            }
        }
        return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
    }

}
/**
 * 响应
 */
@Getter
@Setter
@AllArgsConstructor
public class SimpleResponse {

    private Object content;

}

RequestCache接口声明了缓存与恢复操作。默认实现类是HttpSessionRequestCache,接口的声明如下:

public interface RequestCache {
 
 // 将request缓存到session中
void saveRequest(HttpServletRequest request, HttpServletResponse response);
 
 // 从session中取request
 SavedRequest getRequest(HttpServletRequest request, HttpServletResponse response);
 
 // 获得与当前request匹配的缓存,并将匹配的request从session中删除
HttpServletRequest getMatchingRequest(HttpServletRequest request,
 HttpServletResponse response);
 
 // 删除缓存的request
 void removeRequest(HttpServletRequest request, HttpServletResponse response);
}

对request的缓存,可参考Spring Security 源码解析(一)

 

建立好之后的文件目录如下所示:

 

spring-security-demo

在src/main/resources下再建立一个resources目录,用来放静态文件。然后在resources下建立plogin.html页面

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>登录</title>
</head>
<body>
    <h2>自定义登录页面</h2>
</body>
</html>

 

启动服务,访问 http://localhost:18081/user,跳转到如下页面:

可以看到,访问/user时,需要授权认证,然后跳转到/authentication/require,直接返回401状态码和我们自定义的content数据

 

 访问 http://localhost:18081/index.html,跳转到如下页面:

可以看到,访问 /index.html 时,需要授权认证,然后跳转到 /authentication/require,发现是html请求,重定向到 login.html 页面

 

接下面,在服务调用者的配置文件application.yml中,加上自定义的登录页配置:

security:
  browser: 
    loginPage: /plogin.html

然后重启项目,访问http://localhost:18081/index.html,跳转到如下页面:

可以看到,现在跳转到了自定义的登录页面

 

 

posted @ 2018-10-23 08:51  仅此而已-远方  阅读(2553)  评论(1编辑  收藏  举报