项目搭建
1、导入相关依赖 pom.xml:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
2、Spring容器配置
@Configuration
@ComponentScan(basePackages = {"com.example"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
public class ApplicationConfig {
// 配置除了Controller的其他bean,如数据库连接池、事务管理器、业务Bean等
}
3、SpringMVC容器配置
@Configuration
@ComponentScan(basePackages = {"com.example"},
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)},
useDefaultFilters = false)
@EnableWebMvc
public class WebMvConfig implements WebMvcConfigurer {
// SpringMVC相关注解
/**
* 配置视图解析器
* @return
*/
@Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
4、加载Spring容器:在 init 包下创建 Spring 容器初始化类 SpringApplicationInitializer,此类实现 WebApplicationInitializer 接口,Spring 容器启动时加载 WebApplicationInitializer 接口的所有实现类
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
// Spring父容器配置类
return new Class[]{ApplicationConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
// SpringMVC配置类
return new Class[]{WebMvConfig.class};
}
@Override
protected String[] getServletMappings() {
// Url-Mapping
return new String[]{"/"};
}
}
5、在 src/main/webapp/WEB-INF/views/ 目录下创建 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<form action="login" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
6、在 WebMvcConfig 中新增如下配置,将 / 导向 login.jsp 页面:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 将 / 导向login.jsp
registry.addViewController("/").setViewName("login");
}
7、测试
实现认证功能
1、创建 dto 目录,在下面创建两个数据模型:UserDto 和 AuthenticationRequest
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class UserDto {
// 用户身份信息
private String id;
private String username;
private String password;
private String fullname;
private String phone;
}
@Data
@Accessors(chain = true)
public class AuthenticationRequest {
// 用户身份信息
private String username;
private String password;
}
2、创建相关service:
public interface AuthenticationService {
UserDto authentication(AuthenticationRequest authenticationRequest);
}
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private Map<String, UserDto> userMap = new HashMap<>();
{
userMap.put("zhangsan", new UserDto("1010", "zhangsan", "afafafada", "zhangsan", "123364555"));
userMap.put("wangwu", new UserDto("1011", "wangwu", "vbaskvsal", "wangwu", "1233645662"));
}
// 模拟用户查询
public UserDto getUserDto(String username) {
return userMap.get(username);
}
@Override
public UserDto authentication(AuthenticationRequest authenticationRequest) {
// 校验用户信息
if (authenticationRequest == null ||
StringUtils.isEmpty(authenticationRequest.getUsername()) ||
StringUtils.isEmpty(authenticationRequest.getPassword())) {
// 较佳方法,在JavaBean模型创建时对相关参数作校验,如果其中参数不符合条件,则模型无法创建成功,在service中只对模型是否为空作校验
throw new RuntimeException("参数非法");
}
// 模拟查询数据库
UserDto userDto = this.getUserDto(authenticationRequest.getUsername());
// 用户数据不存在
if (userDto == null) {
throw new RuntimeException("用户不存在");
}
// 账号密码错误
if (!authenticationRequest.getPassword().equals(userDto.getPassword())) {
throw new RuntimeException("账号或密码错误");
}
return userDto;
}
}
3、创建Controller
@RestController
public class LoginController {
@Autowired
private AuthenticationService authenticationService;
@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")
public String login(AuthenticationRequest request) {
UserDto authentication = authenticationService.authentication(request);
return authentication.getUsername() + "登录成功!";
}
}
实现会话控制
会话指在用户登录系统后,系统会记住用户的登录状态,在系统连续操作直到退出系统的过程
1、增加会话控制:
在 UserDto 中定义一个 SESSION_USER_KEY ,作为 Session 中存放登录用户信息的 key
public static final String SESSION_USER_KEY = "_user";
修改LoginController,认证成功后,将用户信息放入当前会话,并增加用户退出登录的方法,在该方法中将 session 设置为失效
@RequestMapping(value = "/login", produces = "text/plain;charset=utf-8")
public String login(AuthenticationRequest request, HttpSession session) {
UserDto authentication = authenticationService.authentication(request);
// 存入session
session.setAttribute(UserDto.SESSION_USER_KEY, authentication);
return authentication.getUsername() + "登录成功!";
}
@RequestMapping(value = "/r/r1",produces = "text/plain;charset=utf-8")
public String r1(HttpSession session){
Object userDto = session.getAttribute(UserDto.SESSION_USER_KEY);
if (userDto == null) {
return "匿名";
}
return ((UserDto) userDto).getFullname();
}
@RequestMapping(value = "/logout",produces = "text/plain;charset=utf-8")
public String logout(HttpSession session){
session.invalidate();
return "退出登录";
}
实现授权功能
1、在 UserDto 上添加用户权限字段
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class UserDto {
public static final String SESSION_USER_KEY = "_user";
// 用户身份信息
private String id;
private String username;
private String password;
private String fullname;
private String phone;
// 用户权限
private Set<String> authorities;
}
2、修改 AuthenticationServiceImpl,新增权限字段
private Map<String, UserDto> userMap = new HashMap<>();
{
// 新建两个权限
Set<String> authorities1 = new HashSet<>();
authorities1.add("p1"); // /r/r1
Set<String> authorities2 = new HashSet<>();
authorities2.add("p2"); // /r/r2
userMap.put("zhangsan", new UserDto("1010", "zhangsan", "afafafada", "zhangsan", "123364555", authorities1));
userMap.put("wangwu", new UserDto("1011", "wangwu", "vbaskvsal", "wangwu", "1233645662", authorities2));
}
3、使用 mvc 提供的拦截器,即实现 HandlerInterceptor 接口:
@Component
public class SimpleAuthenticationInterceptor implements HandlerInterceptor {
/**
* 在调用方法之前调用
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在这个方法中校验用户请求的URL是否在用户的权限范围内
// 取出用户的身份信息
Object object = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);
System.out.println(object);
if (object == null) {
writeContent(response, "请登录");
}
UserDto userDto = (UserDto) object;
// 获取用户权限
String requestURI = request.getRequestURI();
if (userDto.getAuthorities().contains("p1") && requestURI.contains("/r/r1")) {
return true;
}
if (userDto.getAuthorities().contains("p2") && requestURI.contains("/r/r2")) {
return true;
}
writeContent(response, "没有权限,拒绝访问!");
return false;
}
private void writeContent(HttpServletResponse response, String msg) throws IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.print(msg);
writer.flush();
writer.close();
}
}
4、在 WebMvConfig 中进行配置,将拦截器加入SpringMVC
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");
}