• Spring Security入门教程,springboot整合Spring Security


    Spring Security是Spring官方推荐的认证、授权框架,功能相比Apache Shiro功能更丰富也更强大,但是使用起来更麻烦。

    如果使用过Apache Shiro,学习Spring Security会比较简单一点,两种框架有很多相似的地方。

    目录

    一、准备工作

    创建springboot项目

    pom.xml

    application.yml

    二、创建相关的类

    UserDetailsService

    SecurityConfig.java

    SystemProperties.java

    MybatisPlusConfig.java

    三、完成登录接口

    创建数据库实体类

    创建持久层接口

    创建登录DTO对象

    创建控制器类

    创建业务层类

    自定义登录成功处理器


    一、准备工作

    创建springboot项目

    首先,通过IntelliJ IDEA创建一个springboot项目,项目名为springboot-springsecurity,在pom.xml中添加相关依赖。

    pom.xml

    1. "1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    4. <modelVersion>4.0.0modelVersion>
    5. <parent>
    6. <groupId>org.springframework.bootgroupId>
    7. <artifactId>spring-boot-starter-parentartifactId>
    8. <version>2.3.4.RELEASEversion>
    9. <relativePath />
    10. parent>
    11. <groupId>com.examplegroupId>
    12. <artifactId>springboot-springsecurityartifactId>
    13. <version>0.0.1-SNAPSHOTversion>
    14. <properties>
    15. <java.version>1.8java.version>
    16. <jjwt.version>0.9.1jjwt.version>
    17. <mysql.version>8.0.28mysql.version>
    18. <druid.version>1.1.21druid.version>
    19. <lombok.version>1.18.22lombok.version>
    20. <mybatis.version>2.2.2mybatis.version>
    21. <mybatis-plus.version>3.5.1mybatis-plus.version>
    22. properties>
    23. <dependencies>
    24. <dependency>
    25. <groupId>org.springframework.bootgroupId>
    26. <artifactId>spring-boot-starter-webartifactId>
    27. dependency>
    28. <dependency>
    29. <groupId>org.springframework.bootgroupId>
    30. <artifactId>spring-boot-starter-validationartifactId>
    31. dependency>
    32. <dependency>
    33. <groupId>org.springframework.bootgroupId>
    34. <artifactId>spring-boot-starter-securityartifactId>
    35. dependency>
    36. <dependency>
    37. <groupId>mysqlgroupId>
    38. <artifactId>mysql-connector-javaartifactId>
    39. <version>${mysql.version}version>
    40. dependency>
    41. <dependency>
    42. <groupId>com.alibabagroupId>
    43. <artifactId>druidartifactId>
    44. <version>${druid.version}version>
    45. dependency>
    46. <dependency>
    47. <groupId>org.projectlombokgroupId>
    48. <artifactId>lombokartifactId>
    49. <version>${lombok.version}version>
    50. dependency>
    51. <dependency>
    52. <groupId>org.mybatis.spring.bootgroupId>
    53. <artifactId>mybatis-spring-boot-starterartifactId>
    54. <version>${mybatis.version}version>
    55. dependency>
    56. <dependency>
    57. <groupId>com.baomidougroupId>
    58. <artifactId>mybatis-plus-boot-starterartifactId>
    59. <version>${mybatis-plus.version}version>
    60. dependency>
    61. <dependency>
    62. <groupId>io.jsonwebtokengroupId>
    63. <artifactId>jjwtartifactId>
    64. <version>${jjwt.version}version>
    65. dependency>
    66. dependencies>
    67. <build>
    68. <plugins>
    69. <plugin>
    70. <groupId>org.springframework.bootgroupId>
    71. <artifactId>spring-boot-maven-pluginartifactId>
    72. plugin>
    73. plugins>
    74. build>
    75. project>

    application.yml

    1. server:
    2. port: 8080
    3. servlet:
    4. context-path: /
    5. spring:
    6. datasource:
    7. username: root
    8. password: root
    9. url: jdbc:mysql://localhost:3306/spring_security
    10. driver-class-name: com.mysql.cj.jdbc.Driver
    11. type: com.alibaba.druid.pool.DruidDataSource
    12. mybatis-plus:
    13. mapper-locations: classpath:mapper/*Mapper.xml
    14. logging:
    15. level:
    16. springfox: error
    17. com.example.security: debug
    18. system:
    19. login-page: /login.html
    20. login-url: /user/login
    21. index-page: /index.html
    22. logout-url: /user/logout
    23. parameter:
    24. username: username
    25. password: password
    26. white-url:
    27. - /js/**
    28. - /css/**
    29. - /images/**
    30. - /user/login
    31. - /login.html

    二、创建相关的类

    UserDetailsService

    UserDetailsService接口是Spring Security中非常重要的接口,在登录认证的时候会通过这个接口的loadUserByUsername()方法获取用户的信息,来完成登录的用户名、密码校验,完成登录流程。

    我们需要创建一个UserDetailsService的实现类,并声明为Spring组件。

    1. package com.example.security.security;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.example.security.entity.User;
    4. import com.example.security.exception.GlobalException;
    5. import com.example.security.mapper.UserMapper;
    6. import com.example.security.restful.ResponseCode;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.security.authentication.BadCredentialsException;
    9. import org.springframework.security.core.userdetails.UserDetails;
    10. import org.springframework.security.core.userdetails.UserDetailsService;
    11. import org.springframework.security.core.userdetails.UsernameNotFoundException;
    12. import org.springframework.stereotype.Component;
    13. import java.util.ArrayList;
    14. import java.util.List;
    15. /**
    16. * @author heyunlin
    17. * @version 1.0
    18. */
    19. @Component
    20. public class UserDetailsServiceImpl implements UserDetailsService {
    21. private final UserMapper userMapper;
    22. @Autowired
    23. public UserDetailsServiceImpl(UserMapper userMapper) {
    24. this.userMapper = userMapper;
    25. }
    26. @Override
    27. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    28. // 查询用户信息
    29. User user = selectByUsername(username);
    30. if (user == null) {
    31. throw new BadCredentialsException("登录失败,用户名不存在!");
    32. } else {
    33. List permissions = selectPermissions(username);
    34. return org.springframework.security.core.userdetails.User.builder()
    35. .username(user.getUsername())
    36. .password(user.getPassword())
    37. .accountExpired(false)
    38. .accountLocked(false)
    39. .disabled(!user.getEnable())
    40. .credentialsExpired(false)
    41. .authorities(permissions.toArray(new String[] {}))
    42. .build();
    43. }
    44. }
    45. /**
    46. * 通过用户名查询用户信息
    47. * @param username 用户名
    48. * @return User
    49. */
    50. private User selectByUsername(String username) {
    51. QueryWrapper wrapper = new QueryWrapper<>();
    52. wrapper.eq("username", username);
    53. List list = userMapper.selectList(wrapper);
    54. if (list.size() == 1) {
    55. return list.get(0);
    56. }
    57. return null;
    58. }
    59. /**
    60. * 通过用户名查询用户权限
    61. * @param username 用户名
    62. * @return List
    63. */
    64. private List selectPermissions(String username) {
    65. if (username == null) {
    66. throw new GlobalException(ResponseCode.BAD_REQUEST, "用户名不能为空");
    67. }
    68. List permissions = new ArrayList<>();
    69. permissions.add("/user/login");
    70. permissions.add("/user/logout");
    71. permissions.add("/user/selectById");
    72. return permissions;
    73. }
    74. }

    SecurityConfig.java

    创建security的配置类

    1. package com.example.security.config;
    2. import com.example.security.security.LoginFailHandler;
    3. import com.example.security.security.LoginSuccessHandler;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.context.annotation.Bean;
    6. import org.springframework.context.annotation.Configuration;
    7. import org.springframework.security.authentication.AuthenticationManager;
    8. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    9. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    10. import org.springframework.security.crypto.password.PasswordEncoder;
    11. /**
    12. * @author heyunlin
    13. * @version 1.0
    14. */
    15. @Configuration
    16. public class SecurityConfig extends WebSecurityConfigurerAdapter {
    17. private final SystemProperties systemProperties;
    18. @Autowired
    19. public SecurityConfig(SystemProperties systemProperties) {
    20. this.systemProperties = systemProperties;
    21. }
    22. @Bean
    23. public PasswordEncoder passwordEncoder() {
    24. return new PasswordEncoder() {
    25. @Override
    26. public String encode(CharSequence charSequence) {
    27. return (String) charSequence;
    28. }
    29. @Override
    30. public boolean matches(CharSequence charSequence, String s) {
    31. return charSequence.equals(s);
    32. }
    33. };
    34. }
    35. @Bean
    36. @Override
    37. public AuthenticationManager authenticationManagerBean() throws Exception {
    38. return super.authenticationManagerBean();
    39. }
    40. @Override
    41. protected void configure(HttpSecurity http) throws Exception {
    42. // 禁用防跨域攻击
    43. http.csrf().disable();
    44. // 配置各请求路径的认证与授权
    45. http.formLogin()
    46. .loginPage(systemProperties.getLoginPage()) // 自定义登录页面的地址
    47. .loginProcessingUrl(systemProperties.getLoginUrl()) // 处理登录的接口地址
    48. .usernameParameter(systemProperties.getParameter().get("username")) // 用户名的参数名
    49. .passwordParameter(systemProperties.getParameter().get("password")) // 密码的参数名
    50. .successHandler(new LoginSuccessHandler(systemProperties))
    51. //.successForwardUrl("/index.html") // 登录成功跳转的地址
    52. .failureHandler(new LoginFailHandler()); // 登录失败的处理器
    53. // 退出登录相关配置
    54. http.logout()
    55. .logoutUrl(systemProperties.getLogoutUrl()) // 退出登录的接口地址
    56. .logoutSuccessUrl(systemProperties.getLoginUrl()); // 退出登录成功跳转的地址
    57. // 配置认证规则
    58. String[] toArray = systemProperties.getWhiteUrl().toArray(new String[]{});
    59. http.authorizeRequests()
    60. .antMatchers(toArray).permitAll() // 白名单,也就是不需要登录也能访问的资源
    61. .anyRequest().authenticated();
    62. }
    63. }

    SystemProperties.java

    1. package com.example.security.config;
    2. import lombok.Data;
    3. import org.springframework.boot.context.properties.ConfigurationProperties;
    4. import org.springframework.stereotype.Component;
    5. import java.util.List;
    6. import java.util.Map;
    7. /**
    8. * @author heyunlin
    9. * @version 1.0
    10. */
    11. @Data
    12. @Component
    13. @ConfigurationProperties(prefix = "system")
    14. public class SystemProperties {
    15. /**
    16. * 登录页面
    17. */
    18. private String loginPage;
    19. /**
    20. * 登录的请求地址
    21. */
    22. private String loginUrl;
    23. /**
    24. * 登录成功后跳转的页面
    25. */
    26. private String indexPage;
    27. /**
    28. * 退出登录的请求地址
    29. */
    30. private String logoutUrl;
    31. /**
    32. * 白名单
    33. */
    34. private List whiteUrl;
    35. /**
    36. * 登录的参数
    37. */
    38. private Map parameter;
    39. }

    MybatisPlusConfig.java

    1. package com.example.security.config;
    2. import com.baomidou.mybatisplus.annotation.DbType;
    3. import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    4. import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
    5. import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    6. import org.mybatis.spring.annotation.MapperScan;
    7. import org.springframework.context.annotation.Bean;
    8. import org.springframework.context.annotation.Configuration;
    9. /**
    10. * @author heyunlin
    11. * @version 1.0
    12. */
    13. @Configuration
    14. @MapperScan(basePackages = "com.example.security.mapper")
    15. public class MybatisPlusConfig {
    16. @Bean
    17. public MybatisPlusInterceptor mybatisPlusInterceptor() {
    18. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    19. // 防全表更新与删除插件
    20. interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
    21. // 分页插件
    22. interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    23. return interceptor;
    24. }
    25. }

    三、完成登录接口

    创建数据库实体类

    User.java

    1. package com.example.security.entity;
    2. import com.baomidou.mybatisplus.annotation.IdType;
    3. import com.baomidou.mybatisplus.annotation.TableId;
    4. import com.baomidou.mybatisplus.annotation.TableName;
    5. import com.fasterxml.jackson.annotation.JsonFormat;
    6. import lombok.Data;
    7. import java.io.Serializable;
    8. import java.time.LocalDateTime;
    9. /**
    10. * 用户
    11. * @author heyunlin
    12. * @version 1.0
    13. */
    14. @Data
    15. @TableName("user")
    16. public class User implements Serializable {
    17. private static final long serialVersionUID = 18L;
    18. @TableId(value = "id", type = IdType.INPUT)
    19. private String id;
    20. /**
    21. * 姓名
    22. */
    23. private String name;
    24. /**
    25. * 性别
    26. */
    27. private Integer gender;
    28. /**
    29. * 用户名
    30. */
    31. private String username;
    32. /**
    33. * 密码
    34. */
    35. private String password;
    36. /**
    37. * 手机号
    38. */
    39. private String phone;
    40. /**
    41. * 是否启用
    42. */
    43. private Boolean enable;
    44. /**
    45. * 最后一次登录时间
    46. */
    47. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    48. private LocalDateTime lastLoginTime;
    49. }

    创建持久层接口

    1. package com.example.security.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.example.security.entity.User;
    4. import org.springframework.stereotype.Repository;
    5. /**
    6. * @author heyunlin
    7. * @version 1.0
    8. */
    9. @Repository
    10. public interface UserMapper extends BaseMapper {
    11. }

    创建登录DTO对象

    1. package com.example.security.dto;
    2. import lombok.Data;
    3. import javax.validation.constraints.NotEmpty;
    4. import javax.validation.constraints.NotNull;
    5. import java.io.Serializable;
    6. /**
    7. * @author heyunlin
    8. * @version 1.0
    9. */
    10. @Data
    11. public class UserLoginDTO implements Serializable {
    12. private static final long serialVersionUID = 18L;
    13. /**
    14. * 用户名
    15. */
    16. @NotNull(message = "用户名不允许为空")
    17. @NotEmpty(message = "用户名不允许为空")
    18. private String username;
    19. /**
    20. * 密码
    21. */
    22. @NotNull(message = "密码不允许为空")
    23. @NotEmpty(message = "密码不允许为空")
    24. private String password;
    25. }

    创建控制器类

    1. package com.example.security.controller;
    2. import com.example.security.dto.UserLoginDTO;
    3. import com.example.security.entity.User;
    4. import com.example.security.restful.JsonResult;
    5. import com.example.security.service.UserService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.validation.annotation.Validated;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RequestMethod;
    10. import org.springframework.web.bind.annotation.RequestParam;
    11. import org.springframework.web.bind.annotation.RestController;
    12. /**
    13. * @author heyunlin
    14. * @version 1.0
    15. */
    16. @RestController
    17. @RequestMapping(path = "/user", produces = "application/json;charset=utf-8")
    18. public class UserController {
    19. private final UserService userService;
    20. @Autowired
    21. public UserController(UserService userService) {
    22. this.userService = userService;
    23. }
    24. @RequestMapping(value = "/login", method = RequestMethod.POST)
    25. public JsonResult login(@Validated UserLoginDTO userLoginDTO) {
    26. userService.login(userLoginDTO);
    27. return JsonResult.success("登录成功");
    28. }
    29. @RequestMapping(value = "/logout", method = RequestMethod.POST)
    30. public JsonResult logout() {
    31. userService.logout();
    32. return JsonResult.success("登出成功");
    33. }
    34. @RequestMapping(value = "/selectById", method = RequestMethod.GET)
    35. public JsonResult selectById(@RequestParam(value = "id", required = true) String userId) {
    36. User user = userService.selectById(userId);
    37. return JsonResult.success(null, user);
    38. }
    39. }

    创建业务层类

    UserService接口

    1. package com.example.security.service;
    2. import com.example.security.dto.UserLoginDTO;
    3. import com.example.security.entity.User;
    4. /**
    5. * @author heyunlin
    6. * @version 1.0
    7. */
    8. public interface UserService {
    9. /**
    10. * 登录认证
    11. * @param userLoginDTO 用户登录信息
    12. */
    13. void login(UserLoginDTO userLoginDTO);
    14. /**
    15. * 退出登录
    16. */
    17. void logout();
    18. /**
    19. * 通过ID查询用户信息
    20. * @param userId 用户ID
    21. * @return User 通过ID查询到的用户信息
    22. */
    23. User selectById(String userId);
    24. }

    UserServiceImpl.java

    1. package com.example.security.service.impl;
    2. import com.example.security.dto.UserLoginDTO;
    3. import com.example.security.entity.User;
    4. import com.example.security.mapper.UserMapper;
    5. import com.example.security.service.UserService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.security.authentication.AuthenticationManager;
    8. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    9. import org.springframework.security.core.Authentication;
    10. import org.springframework.stereotype.Service;
    11. /**
    12. * @author heyunlin
    13. * @version 1.0
    14. */
    15. @Service
    16. public class UserServiceImpl implements UserService {
    17. private final UserMapper userMapper;
    18. private final AuthenticationManager authenticationManager;
    19. @Autowired
    20. public UserServiceImpl(UserMapper userMapper, AuthenticationManager authenticationManager) {
    21. this.userMapper = userMapper;
    22. this.authenticationManager = authenticationManager;
    23. }
    24. @Override
    25. public void login(UserLoginDTO userLoginDTO) {
    26. Authentication authentication = new UsernamePasswordAuthenticationToken(
    27. userLoginDTO.getUsername(),
    28. userLoginDTO.getPassword()
    29. );
    30. authenticationManager.authenticate(authentication);
    31. }
    32. @Override
    33. public void logout() {
    34. // todo
    35. }
    36. @Override
    37. public User selectById(String userId) {
    38. return userMapper.selectById(userId);
    39. }
    40. }

    自定义登录成功处理器

    登陆成功直接重定向到/index.html

    1. package com.example.security.security;
    2. import com.example.security.config.SystemProperties;
    3. import org.springframework.security.core.Authentication;
    4. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    5. import javax.servlet.http.HttpServletRequest;
    6. import javax.servlet.http.HttpServletResponse;
    7. import java.io.IOException;
    8. /**
    9. * @author heyunlin
    10. * @version 1.0
    11. */
    12. public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    13. private final SystemProperties systemProperties;
    14. public LoginSuccessHandler(SystemProperties systemProperties) {
    15. this.systemProperties = systemProperties;
    16. }
    17. @Override
    18. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
    19. response.sendRedirect(systemProperties.getIndexPage());
    20. }
    21. }

    至此,springboot整合Spring Security就完成了,项目结构如下。

    文章就分享到这里了,代码已开源,可按需获取~

    springboot整合spring securityicon-default.png?t=N7T8https://gitee.com/he-yunlin/springboot-springsecurity.git

  • 相关阅读:
    【pip command】之 options 汇总
    【数据结构】树和二叉树的应用(哈夫曼树和哈夫曼编码、堆和优先级队列)
    Nginx+Tomcat 多实例负载均衡,动静分离部署
    为什么工业物联网平台要配备MQTT网关?|
    HIVE如何校验map类型数据 -- 比较json和map的转换前后数据一致性
    学习记录11
    java基础面试题(一)
    谷歌浏览器误代码STATUS_INVALID_IMAGE_HASH如何一行代码解决
    Spring基础(四):XML方式实现DI
    JSP session对象
  • 原文地址:https://blog.csdn.net/heyl163_/article/details/133956908