0%

【Spring Boot自学笔记三】登录与拦截器

登录

登录很简单,用一个控制器类,匹配传入的参数是否与数据库中的用户信息匹配,如果匹配就跳转到后台,不匹配就返回主页。

这里笔者简单的使用模拟数据来提供一个用户。

spring boot会自动根据控制器方法中提供的参数匹配request传入的数据,若标注@RequestParam,传入数据为空则会自动报错。

实现登录控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.koorye.hellospringboot.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;

import javax.servlet.http.HttpSession;
import java.util.Map;

@Controller
public class LoginController {
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password) {
if (username.equals("admin") && password.equals("admin")) {
return "redirect:/user/list";
} else {
return "index";
}
}
}

指定html中的input标签

有了控制器,我们要怎么传递参数呢?spring boot会根据<input>标签中的name属性找到参数的来源对象,因此,我们需要给登录界面的text文本框添加name属性,来指定username和password的来源:

1
2
3
4
5
6
7
8
9
10
11
<form action="/login" method="post">
<h2 class="display-4 mb-4" th:text="#{index.login}">Login</h2>
<label>
<input class="form-control mb-2" name="username" type="text" placeholder="Username"
th:placeholder="#{index.username}">
<input class="form-control" name="password" type="password" placeholder="Password"
th:placeholder="#{index.password}">
</label>
<br>
<input class="btn btn-primary" type="submit" value="Sign In" th:value="#{index.signIn}">
</form>

登录失败提示

我们可以使用请求转发的方式得到一个报错信息,再利用thymeleaf解析生成。

控制器添加参数

我们给login方法添加一个map,通过map.put()方法给map指定key和value,就相当于在servlet中给request指定attributeName属性名和attributeValue属性值,最后返回到主页,主页就可以根据request中的参数生成提示:

1
2
3
4
5
6
7
8
9
10
11
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map<String, Object> map) {
if (username.equals("admin") && password.equals("admin")) {
return "redirect:/user/list";
} else {
map.put("loginMsg", "Login Failed! Please check username and password.");
return "index";
}
}

主页接收参数

主页接收提示,使用thymeleaf解析:

  • th:if: 如果条件满足就显示标签,否则不显示
  • th:text: 指定标签中的文本内容
  • ${}: 变量表达式,此处用于获取request属性值
1
<p style="color: red" th:text="${loginMsg}" th:if="${loginMsg}!=null"></p>

登录失败效果(笔者已进行国际化处理,因此是中文): 在这里插入图片描述


拦截器

上面的登录还有一个问题:如果用户不登录,直接输入后台的url也可以得到后台页面,这使得登录界面毫无意义。因此,我们可以实现一个拦截器来拦截未登录的用户。

通过控制器设置session属性

我们给登录控制器添加一个HttpSession参数,并通过session.setAttribute()添加user属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Map<String, Object> map,
HttpSession session) {
if (username.equals("admin") && password.equals("admin")) {
session.setAttribute("user", username);
return "redirect:/user/list";
} else {
map.put("loginMsg", "Login Failed! Please check username and password.");
return "index";
}
}

实现拦截器类

之后,我们就可以通过判断session中user属性是否存在来作出操作:

如果user存在,跳转到后台;如果user不存在,返回主页并传递错误信息。

创建一个登录拦截器类,实现HandlerInterceptor拦截器的接口,重载preHandle方法,返回true表示放行,false则表示拦截。

当然,别忘了在主页实现显示错误信息的标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.koorye.hellospringboot.component;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("user");
if (user == null) {
request.setAttribute("userMsg", "Please visit the page after login!");
request.getRequestDispatcher("/index").forward(request, response);
return false;
} else {
return true;
}
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}

把拦截器加入组件中

单是这样,拦截器并没有启用,我们必须把拦截器加入组件中,在MvcConfig类的webMvcConfig方法中重载addInterceptors方法:

  • addPathPatterns: 指定需要拦截的路径,/**表示根路径下的所有路径
  • excludePathPatterns: 排除不需要拦截的路径,这里首页//index、登录请求/login、webjars和asserts中的css和js/webjars/**/asserts/**是不需要拦截的,如果拦截则会导致无法登录、样式无法加载等问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package org.koorye.hellospringboot.config;

import org.koorye.hellospringboot.component.LoginHandlerInterceptor;
import org.koorye.hellospringboot.component.MyLocaleResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig {
...
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
...
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/index", "/", "/login", "/webjars/**,/asserts/**");
}
};
...
}

大功告成,拦截效果: 在这里插入图片描述 登录成功效果: 在这里插入图片描述