Fms-Java版开发实录:05 用户功能URL设计、登出界面、错误页面

Fms-Java版开发实录:05 用户功能URL设计、登出界面、错误页面

设计先行

对于Web应用,URL对应资源,设计良好的URL是非常关键的,而且在现代设计理念中,应当尽可能少的使用URL传参,而是与资源地址进行交互,尤其REST风格更是如此。

虽然我这个项目不搞前后端分离,不过还是尽量设计好URL

用户功能URL设计

我想了一下,把用户功能的URL设计如下:

  1. /account/login,用于GET请求返回用户登录页面,已经编写完成。
  2. /account/authSpring Security配置的接受POST验证的路径,已经编写完成。
  3. /account/logout,准备编写一个页面,用于确认登出。
  4. /account/signoutSpring Security配置的用于登出的路径。
  5. /account/register,自行编写,GET请求用于获取用户注册页面,POST请求用于接受用户注册表单,目前额外信息就一个:用户的邮箱。
  6. /account/profile,修改用户信息和密码的界面,GET请求返回表单,POST请求用于接受用户的修改。
  7. /account/reset,用户重置密码的界面,这个需要发一个邮件给用户,让其点击其中的路径,如何生成路径的业务逻辑,我需要再想一想,还涉及发送邮件的配置,都需要学习一下,可以等思考成熟了再编写。

编写登出功能

在开启CSRF(默认开启)的情况下,可以配置Spring Security用于登出的地址,而且这个地址需要接受一个POST请求就可以取消登录状态,为此先来配置Spring Security的登出地址:

配置Spring Securitylogout功能

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/css/**", "/img/**", "/js/**", "/webfonts/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/account/login").loginProcessingUrl("/account/auth").permitAll()
            .and()
            .logout().logoutUrl("/account/signout").permitAll();
}

现在/account/signout接受POST请求就可以取消登录状态。
按照刚才的设计,要为/account/logout编写控制器和页面,其中用一个按钮来选择是否登出。

编写页面和控制器

登出的页面可以参考登录的页面,只需要保留一个按钮和说明文字即可,我们把这个页面叫做logout.html

<main class="form-signin">
    <form action="/account/signout" th:action="@{/account/signout}" method="post">
        <h1 class="h3 mb-3 fw-normal">确认登出</h1>
        <button class="w-100 btn btn-lg btn-primary" type="submit">登出</button>
    </form>
</main>

表单action的地址需要变成/account/signout,与Spring Security的配置保持一致。
然后就是来编写返回这个页面的控制器,这个就比较简单了,无需做任何处理。而且这个页面在目前的配置下,不登录是无法访问的。仅仅只有/account/login/account/signout是配置成了permitAll()

@RequestMapping("/logout")
public String logout() {
    return "/account/logout";
}

编写好之后运行项目,先按照提示登录,然后输入/account/logout地址,就可以进入登出页面,点击按钮即可登出。

修改在登录状态下访问登录页面的逻辑

一般在登录状态下访问登录页面,可以有几种选择,一是提示已经登录,让其选择返回或者登出,二是重定向到登出页面,三是重定向到其他页面,比如首页。
这里我选择重定向到首页。

这里的核心逻辑就是需要在控制器中判断当前请求是否已经通过身份验证,由于采用了Spring Security的登录验证,其结果是符合Java Web容器中的request.getPrinciple()的规则,所以就可以用其来进行判断,修改account/login路径的控制器如下:

@RequestMapping("/login")
public String login(@RequestParam(required = false, name = "error") String failed, HttpServletRequest request, Model model) {
    //        如果用户已经登录,直接重定向到首页
    if (request.getUserPrincipal() != null) {
        return "redirect:/";
    }
    
    if (failed != null) {
        model.addAttribute("failed", "用户名或密码错误,或用户未激活,请联系管理员");
    }
    return "/account/login";
}

这里采用了简单一点的方式,直接重定向到根目录。不过目前我们还没有编写根目录的控制器,因此会返回一个Spring默认的错误页面,下一步我们就要自定义错误页面。

自定义错误页面

今天在想着这个事情,结果正好看到《Spring Boot 实战》这本书的62页,其中提到了存在Thymeleaf的情况下,在/templates/目录下放一个error.htmlThymeleaf就会去渲染这个页面。对比在/templates/error/下放置状态码对应的页面文件,Thymeleaf渲染的这个页面可以完整的引用样式,而状态码对应的文件由于渲染的层级不同,并不是使用Thymeleaf,所以不是很方便。这里我就按照放置error.html的方式。

编写这个页面的时候,目前我们的错误会有两种,一种是不具备权限=403错误,当然其实我现在的功能也没有用到权限配置,还有一种就是404错误。为此需要使用th:if标签来进行判断,页面则依然可以采用之前与login.html类似的居中的样式:

<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head th:replace="/shared/base::standardHead('错误')"></head>


<body>
    <main class="container">
        <h3 class="text-center mt-3 mb-3">出错啦</h3>
        <ul class="list-group">
            <li class="list-group-item">状态:[[${status}]]</li>
            <li class="list-group-item">路径:[[${path}]]</li>
            <li class="list-group-item">异常:[[${error}]]</li>
            <li class="list-group-item">时间:[[${timestamp}]]</li>
            <li class="list-group-item">类名:[[${exception}]]</li>
            <li class="list-group-item">信息:[[${message}]]</li>
        </ul>
        <p th:if="${status} == '404'" class="text-center mt-2">访问的页面不存在</p>
        <p th:if="${status} == '500'" class="text-center mt-2">服务器错误,请将错误信息<a href="mailto:liyiming@sinochem.com">发送给管理员</a></p>
        <p th:if="${status} == '403'" class="text-center mt-2">不具备权限,请<a href="mailto:liyiming@sinochem.com">联系管理员</a></p>
        <p class="text-center"><a class="btn btn-primary" href="/" th:href="@{/}">返回首页</a></p>
    </main>
</body>
</html>

这里使用了Spring传给页面的一系列属性,然后根据状态码来判断到底是哪一种错误,现在登录之后自动跳转到首页,就会看到这个错误页面。还有一个trace属性是详细错误信息,这个没有必要展示给用户了。

LICENSED UNDER CC BY-NC-SA 4.0
Comment