项目搭建

1、导入相关依赖 pom.xml:

  1. <properties>
  2. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  3. <maven.compiler.source>8</maven.compiler.source>
  4. <maven.compiler.target>8</maven.compiler.target>
  5. </properties>
  6. <dependencies>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-webmvc</artifactId>
  10. <version>5.3.1</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>javax.servlet</groupId>
  14. <artifactId>javax.servlet-api</artifactId>
  15. <version>4.0.1</version>
  16. <scope>provided</scope>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.projectlombok</groupId>
  20. <artifactId>lombok</artifactId>
  21. <version>1.18.16</version>
  22. </dependency>
  23. </dependencies>

2、Spring容器配置

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.example"},
  3. excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)})
  4. public class ApplicationConfig {
  5. // 配置除了Controller的其他bean,如数据库连接池、事务管理器、业务Bean等
  6. }

3、SpringMVC容器配置

  1. @Configuration
  2. @ComponentScan(basePackages = {"com.example"},
  3. includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)},
  4. useDefaultFilters = false)
  5. @EnableWebMvc
  6. public class WebMvConfig implements WebMvcConfigurer {
  7. // SpringMVC相关注解
  8. /**
  9. * 配置视图解析器
  10. * @return
  11. */
  12. @Bean
  13. public InternalResourceViewResolver viewResolver(){
  14. InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
  15. viewResolver.setPrefix("/WEB-INF/views/");
  16. viewResolver.setSuffix(".jsp");
  17. return viewResolver;
  18. }
  19. }

4、加载Spring容器:在 init 包下创建 Spring 容器初始化类 SpringApplicationInitializer,此类实现 WebApplicationInitializer 接口,Spring 容器启动时加载 WebApplicationInitializer 接口的所有实现类

  1. public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
  2. @Override
  3. protected Class<?>[] getRootConfigClasses() {
  4. // Spring父容器配置类
  5. return new Class[]{ApplicationConfig.class};
  6. }
  7. @Override
  8. protected Class<?>[] getServletConfigClasses() {
  9. // SpringMVC配置类
  10. return new Class[]{WebMvConfig.class};
  11. }
  12. @Override
  13. protected String[] getServletMappings() {
  14. // Url-Mapping
  15. return new String[]{"/"};
  16. }
  17. }

5、在 src/main/webapp/WEB-INF/views/ 目录下创建 login.jsp

  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>登录</title>
  5. </head>
  6. <body>
  7. <form action="login" method="post">
  8. 用户名:<input type="text" name="username"><br/>
  9. 密码:<input type="password" name="password"><br/>
  10. <input type="submit" value="登录">
  11. </form>
  12. </body>
  13. </html>

6、在 WebMvcConfig 中新增如下配置,将 / 导向 login.jsp 页面:

  1. @Override
  2. public void addViewControllers(ViewControllerRegistry registry) {
  3. // 将 / 导向login.jsp
  4. registry.addViewController("/").setViewName("login");
  5. }

7、测试

实现认证功能

1、创建 dto 目录,在下面创建两个数据模型:UserDto 和 AuthenticationRequest

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @Accessors(chain = true)
  5. public class UserDto {
  6. // 用户身份信息
  7. private String id;
  8. private String username;
  9. private String password;
  10. private String fullname;
  11. private String phone;
  12. }
  13. @Data
  14. @Accessors(chain = true)
  15. public class AuthenticationRequest {
  16. // 用户身份信息
  17. private String username;
  18. private String password;
  19. }

2、创建相关service:

  1. public interface AuthenticationService {
  2. UserDto authentication(AuthenticationRequest authenticationRequest);
  3. }
  4. @Service
  5. public class AuthenticationServiceImpl implements AuthenticationService {
  6. private Map<String, UserDto> userMap = new HashMap<>();
  7. {
  8. userMap.put("zhangsan", new UserDto("1010", "zhangsan", "afafafada", "zhangsan", "123364555"));
  9. userMap.put("wangwu", new UserDto("1011", "wangwu", "vbaskvsal", "wangwu", "1233645662"));
  10. }
  11. // 模拟用户查询
  12. public UserDto getUserDto(String username) {
  13. return userMap.get(username);
  14. }
  15. @Override
  16. public UserDto authentication(AuthenticationRequest authenticationRequest) {
  17. // 校验用户信息
  18. if (authenticationRequest == null ||
  19. StringUtils.isEmpty(authenticationRequest.getUsername()) ||
  20. StringUtils.isEmpty(authenticationRequest.getPassword())) {
  21. // 较佳方法,在JavaBean模型创建时对相关参数作校验,如果其中参数不符合条件,则模型无法创建成功,在service中只对模型是否为空作校验
  22. throw new RuntimeException("参数非法");
  23. }
  24. // 模拟查询数据库
  25. UserDto userDto = this.getUserDto(authenticationRequest.getUsername());
  26. // 用户数据不存在
  27. if (userDto == null) {
  28. throw new RuntimeException("用户不存在");
  29. }
  30. // 账号密码错误
  31. if (!authenticationRequest.getPassword().equals(userDto.getPassword())) {
  32. throw new RuntimeException("账号或密码错误");
  33. }
  34. return userDto;
  35. }
  36. }

3、创建Controller

  1. @RestController
  2. public class LoginController {
  3. @Autowired
  4. private AuthenticationService authenticationService;
  5. @RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")
  6. public String login(AuthenticationRequest request) {
  7. UserDto authentication = authenticationService.authentication(request);
  8. return authentication.getUsername() + "登录成功!";
  9. }
  10. }

实现会话控制

会话指在用户登录系统后,系统会记住用户的登录状态,在系统连续操作直到退出系统的过程

1、增加会话控制:
在 UserDto 中定义一个 SESSION_USER_KEY ,作为 Session 中存放登录用户信息的 key

  1. public static final String SESSION_USER_KEY = "_user";

修改LoginController,认证成功后,将用户信息放入当前会话,并增加用户退出登录的方法,在该方法中将 session 设置为失效

  1. @RequestMapping(value = "/login", produces = "text/plain;charset=utf-8")
  2. public String login(AuthenticationRequest request, HttpSession session) {
  3. UserDto authentication = authenticationService.authentication(request);
  4. // 存入session
  5. session.setAttribute(UserDto.SESSION_USER_KEY, authentication);
  6. return authentication.getUsername() + "登录成功!";
  7. }
  8. @RequestMapping(value = "/r/r1",produces = "text/plain;charset=utf-8")
  9. public String r1(HttpSession session){
  10. Object userDto = session.getAttribute(UserDto.SESSION_USER_KEY);
  11. if (userDto == null) {
  12. return "匿名";
  13. }
  14. return ((UserDto) userDto).getFullname();
  15. }
  16. @RequestMapping(value = "/logout",produces = "text/plain;charset=utf-8")
  17. public String logout(HttpSession session){
  18. session.invalidate();
  19. return "退出登录";
  20. }

实现授权功能

1、在 UserDto 上添加用户权限字段

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @Accessors(chain = true)
  5. public class UserDto {
  6. public static final String SESSION_USER_KEY = "_user";
  7. // 用户身份信息
  8. private String id;
  9. private String username;
  10. private String password;
  11. private String fullname;
  12. private String phone;
  13. // 用户权限
  14. private Set<String> authorities;
  15. }

2、修改 AuthenticationServiceImpl,新增权限字段

  1. private Map<String, UserDto> userMap = new HashMap<>();
  2. {
  3. // 新建两个权限
  4. Set<String> authorities1 = new HashSet<>();
  5. authorities1.add("p1"); // /r/r1
  6. Set<String> authorities2 = new HashSet<>();
  7. authorities2.add("p2"); // /r/r2
  8. userMap.put("zhangsan", new UserDto("1010", "zhangsan", "afafafada", "zhangsan", "123364555", authorities1));
  9. userMap.put("wangwu", new UserDto("1011", "wangwu", "vbaskvsal", "wangwu", "1233645662", authorities2));
  10. }

3、使用 mvc 提供的拦截器,即实现 HandlerInterceptor 接口:

  1. @Component
  2. public class SimpleAuthenticationInterceptor implements HandlerInterceptor {
  3. /**
  4. * 在调用方法之前调用
  5. *
  6. * @param request
  7. * @param response
  8. * @param handler
  9. * @return
  10. * @throws Exception
  11. */
  12. @Override
  13. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  14. // 在这个方法中校验用户请求的URL是否在用户的权限范围内
  15. // 取出用户的身份信息
  16. Object object = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);
  17. System.out.println(object);
  18. if (object == null) {
  19. writeContent(response, "请登录");
  20. }
  21. UserDto userDto = (UserDto) object;
  22. // 获取用户权限
  23. String requestURI = request.getRequestURI();
  24. if (userDto.getAuthorities().contains("p1") && requestURI.contains("/r/r1")) {
  25. return true;
  26. }
  27. if (userDto.getAuthorities().contains("p2") && requestURI.contains("/r/r2")) {
  28. return true;
  29. }
  30. writeContent(response, "没有权限,拒绝访问!");
  31. return false;
  32. }
  33. private void writeContent(HttpServletResponse response, String msg) throws IOException {
  34. response.setContentType("text/html;charset=UTF-8");
  35. PrintWriter writer = response.getWriter();
  36. writer.print(msg);
  37. writer.flush();
  38. writer.close();
  39. }
  40. }

4、在 WebMvConfig 中进行配置,将拦截器加入SpringMVC

  1. @Override
  2. public void addInterceptors(InterceptorRegistry registry) {
  3. registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");
  4. }