Spring Security快速入门

Spring Security是什么

Spring Security是一个基于Spring的认证授权框架,它主要提供了认证、授权和常见网络攻击防护功能,从而保证了系统的安全性

Spring Security简单使用

  • 初始化一个Spring Boot项目

我这里使用的Spring Boot版本是2.7.5
在这里插入图片描述

  • 引入maven依赖
 <dependency>
       <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <!--   spring security 启动依赖     -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
     </dependency>
     <!--    thymeleaf模板引擎   -->
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-thymeleaf</artifactId>
     </dependency>
  • 在resources目录下创建templates目录

本次教程所有的html页面都放在该目录下
在这里插入图片描述

  • 编写首页index.html
<html xmlns:th="https://www.thymeleaf.org">
<head>
    <title>Hello Security!</title>
</head>
<body>
<h1>Hello Security</h1>
<a th:href="@{/logout}">退出登录</a>
</body>
</html>
  • 启动项目

可以看到控制台输出了用户的密码,其默认用户名是user
在这里插入图片描述

  • 登录系统
    在这里插入图片描述
    在这里插入图片描述

Spring Security为系统提供了默认的登录页、用户信息、登录成功跳转逻辑、登录失败跳转逻辑、注销成功跳转逻辑、未登录校验、权限校验等特性,接下来我们通过配置和编写代码覆盖Spring Security的默认特性

自定义Spring Security

自定义登录页

  • 编写登录接口
@Controller
@RequestMapping("/auth")
public class LoginController {

    /**
     * 跳转到登录页
     *
     * @return
     */
    @GetMapping("/login")
    public String toLogin() {
        return "login"; 
    }
}

  • 编写登录页login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
    <title>Please Log In</title>
</head>
<body>
<h1>Please Log In</h1>
<div th:if="${param.error}">
    Invalid username and password.</div>
<div th:if="${param.logout}">
    You have been logged out.</div>
<form th:action="@{/auth/login}" method="post">
    <div>
        <input type="text" name="user" placeholder="Username"/>
    </div>
    <div>
        <input type="password" name="pwd" placeholder="Password"/>
    </div>
    <input type="submit" value="Log in" />
</form>
</body>
</html>
  • 配置Security过滤器链
@EnableWebSecurity  // 开启Security功能
public class WebSecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorize -> authorize
                        .antMatchers("/auth/login**").permitAll()  // 配置指定请求允许匿名访问
                        .anyRequest().authenticated()  // 配置所有请求需要认证才能访问
                )
                .formLogin(form ->
                        form.loginPage("/auth/login") // 自定义登录页
                                .permitAll()  // 允许任何人访问登录页
                                .usernameParameter("user")  // 自定义用户参数名
                                .passwordParameter("pwd")  // 自定义密码参数名
                )
                .csrf().disable() // 取消 csrf 防护,这样注销可以使用get请求,而无需post请求
        ;
        return http.build();
    }
}
  • 重启项目测试
    在这里插入图片描述

自定义用户数据源

  • 创建数据库

我创建的数据库名是spring-security

  • 创建用户表
CREATE TABLE `system_user` (
           `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
           `username` varchar(100) NOT NULL COMMENT '用户名',
           `pwd` varchar(100) DEFAULT NULL COMMENT '密码',
           `gender` varchar(100) NOT NULL COMMENT '性别',
           PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
  • 添加用户数据(密码是明文123456加密得到的)
INSERT INTO `spring-security`.`system_user`
(id, username, pwd, gender)
VALUES(1, 'xiaolin', '$2a$10$CxlGXE45Q1dqmStmycKrOO/cvlOf3Ywt7jZ42N9Q3LET6GSOW25OK', '男');
INSERT INTO `spring-security`.`system_user`
(id, username, pwd, gender)
VALUES(2, 'xiaolan', '$2a$10$CxlGXE45Q1dqmStmycKrOO/cvlOf3Ywt7jZ42N9Q3LET6GSOW25OK', '女');
  • 添加maven依赖
  <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.5.2</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
  • 配置连接信息
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
    username: root
    password: root
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # mybatis-plus Sql日志打印
  mapper-locations: /mapper/**/*.xml # mapper的xml存放路径
  • 启动类开启Mapper扫描
    在这里插入图片描述

  • 创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SystemUser {
    private Integer id;
    private String username;
    private String pwd;
    private String gender;
}

  • 创建Mapper
public interface SystemUserMapper extends BaseMapper<SystemUser> {
}

  • 在WebSecurityConfig配置类配置密码编码器
    密码编码器帮助我们对密码进行加密存储
   @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
	// 密码编码器使用示例
   public static void main(String[] args) {
        // BCryptPasswordEncoder 对密码进行加密
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        System.out.println(password);
    }
  • 自定义UserDetails
    UserDetails是Spring用于存放用户信息、权限信息的类,如果我们需要额外扩展一些属性,就需要自定义UserDetails
public class CustomUserDetails extends SystemUser implements UserDetails {
	// 添加用户的一个默认角色,可以自定义角色名,不过角色前缀固定为ROLE_
    private List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_common"));

    public CustomUserDetails(SystemUser systemUser) {
        super(systemUser.getId(), systemUser.getUsername(), systemUser.getPwd(), systemUser.getGender());
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return super.getPwd();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  • 自定义用户认证服务
@Service
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private SystemUserMapper systemUserMapper;

    /**
     * 验证用户信息
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SystemUser systemUser = systemUserMapper.selectOne(new LambdaUpdateWrapper<SystemUser>()
                .eq(SystemUser::getUsername, username));
        if (systemUser == null) {
            throw new UsernameNotFoundException(username);
        } else {
            return new CustomUserDetails(systemUser);
        }
    }
}
  • 重启项目进行登录
    在这里插入图片描述

自定义登录成功跳转逻辑

  • 在WebSecurityConfig配置类配置登录成功请求URL
...前面代码省略...
.usernameParameter("user")  // 自定义用户参数名
.passwordParameter("pwd")  // 自定义密码参数名
.defaultSuccessUrl("/auth/index", true)  // 自定义登录成功跳转url
...后面代码省略...
  • 在LoginController添加跳转首页接口
    /**
     * 登录成功跳转到首页
     *
     * @return
     */
    @GetMapping("/index")
    public String toIndex() {
        return "index";
    }

自定义登录失败处理逻辑

自定义登录失败跳转逻辑

  • 在WebSecurityConfig配置类配置登录失败请求URL
...前面代码省略...
.defaultSuccessUrl("/auth/index", true)  // 自定义登录成功跳转url
.failureUrl("/auth/login?login_error")  // 自定义登录失败跳转url
...后面代码省略...
  • 在login.html页面自定义登录失败提示信息
...前面html省略...
<div th:if="${param.error}">
    Invalid username and password.</div>
<div th:if="${param.login_error}">
    自定义:无效的用户名和密码.</div>
<div th:if="${param.logout}">
    You have been logged out.</div>
...后面html省略...

自定义登录失败处理器

  • 自定义认证失败处理器
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
    // 重定向策略
    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        // 重定向到登录页
        this.redirectStrategy.sendRedirect(request, response, "/auth/login?custom_login_error");
    }
}

  • 在WebSecurityConfig配置类配置自定义认证失败处理器
...前面代码省略...
.failureUrl("/auth/login?login_error")  // 自定义登录失败跳转url
.failureHandler(new CustomAuthenticationFailureHandler())  // 自定义登录失败处理器
...后面代码省略...
  • 在login.html页面自定义登录失败提示信息
...前面html省略...
<div th:if="${param.login_error}">
    自定义:无效的用户名和密码.</div>
<div th:if="${param.custom_login_error}">
    自定义处理器:无效的用户名和密码.</div>
<div th:if="${param.logout}">
    You have been logged out.</div>
...后面html省略...

以上两种方式都可以实现登录失败的跳转逻辑处理,使用其中一种即可

自定义注销成功跳转逻辑

  • 在WebSecurityConfig配置类配置注销成功跳转URL
...前面代码省略...
.formLogin(form ->
    form.loginPage("/auth/login") // 自定义登录页
                .permitAll()  // 允许任何人访问登录页
                .usernameParameter("user")  // 自定义用户参数名
                .passwordParameter("pwd")  // 自定义密码参数名
                .defaultSuccessUrl("/auth/index", true)  // 自定义登录成功跳转url
                .failureUrl("/auth/login?login_error")  // 自定义登录失败跳转url
                .failureHandler(new CustomAuthenticationFailureHandler())  // 自定义登录失败处理器
)
.logout(logout ->
        logout.logoutSuccessUrl("/auth/login?login_logout") // 自定义注销成功跳转url
)
...后面代码省略...
  • 在login.html页面自定义注销成功提示信息
...前面html省略...
<div th:if="${param.logout}">
    You have been logged out.</div>
<div th:if="${param.login_logout}">
    自定义:注销成功.</div>
...后面html省略...

自定义未登录跳转逻辑

  • 自定义未登录处理器
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        // 重定向到登录页
        this.redirectStrategy.sendRedirect(request, response, "/auth/login?login_not_alive");
    }
}
  • 在WebSecurityConfig配置类配置自定义未登录处理器
...前面代码省略...
.logout(logout ->
        logout.logoutSuccessUrl("/auth/login?login_logout") // 自定义注销成功跳转url
)
.exceptionHandling(handler -> handler
        .authenticationEntryPoint(new CustomAuthenticationEntryPoint())  // 自定义用户未登录处理器
)
...后面代码省略...
  • 在login.html页面自定义未登录提示信息
...前面html省略...
<div th:if="${param.login_logout}">
    自定义:注销成功.</div>
<div th:if="${param.login_not_alive}">
    自定义:用户已失效.</div>
...后面html省略...

自定义无访问权限跳转逻辑

  • WebSecurityConfig配置类开启安全校验注解
    在这里插入图片描述

  • 编写用户信息和用户列表接口

public interface SystemUserService {
    SystemUser getUser(String username);

    List<SystemUser> getUsers();

}
@Service
public class SystemUserServiceImpl implements SystemUserService {
    @Autowired
    private SystemUserMapper systemUserMapper;

    @Override
    public SystemUser getUser(String username) {
        return systemUserMapper.selectOne(new LambdaQueryWrapper<SystemUser>()
                .eq(SystemUser::getUsername, username));
    }

    @Override
    public List<SystemUser> getUsers() {
        return systemUserMapper.selectList(null);
    }
}
@Controller
@RequestMapping("/user")
public class SystemUserController {

    @Autowired
    private SystemUserService systemUserService;

    /**
     * 获取用户信息
     *
     * @param model
     * @return
     */
    @PreAuthorize("hasAnyRole('admin','common')")
    @GetMapping("/getUserInfo")
    public String getUserInfo(Model model) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        SystemUser user = systemUserService.getUser(username);
        model.addAttribute("user", user);
        return "user";
    }

    @PreAuthorize("hasRole('admin')")
    @GetMapping("/getUserList")
    public String getUserList(Model model) {
        List<SystemUser> users = systemUserService.getUsers();
        model.addAttribute("users", users);
        return "userList";
    }

}
  • 编写用户信息页user.html
<html xmlns:th="https://www.thymeleaf.org">
<head>
    <title>个人主页</title>
</head>
<body>
<p>用户信息</p>
<div th:text="${user.username}"></div>
</body>
</html>
  • 编写用户列表页userList.html
<html xmlns:th="https://www.thymeleaf.org">
<head>
    <title>用户列表</title>
</head>
<body>
<p>用户列表</p>
<div th:each="user:${users}" th:text="${user.username}"></div>
</body>
</html>

  • 编写无权限403页面
<html xmlns:th="https://www.thymeleaf.org">
<head>
    <title>用户无权限!</title>
</head>
<body>
<h1>用户无权限</h1>
<div>
    用户无访问权限
</div>
</body>
</html>

  • 首页index.html添加接口访问链接
...前面html省略...
<h1>Hello Security</h1>
<div>
    <a th:href="@{/user/getUserInfo}">个人主页</a>
    <br/>
    <a th:href="@{/user/getUserList}">用户列表</a>
</div>
<a th:href="@{/logout}">退出登录</a>
...前面html省略...
  • LoginController定义无权限跳转接口
    /**
     * 用户无权限页面
     *
     * @return
     */
    @GetMapping("/accessError")
    public String toAccessError() {
        return "403";
    }
  • 编写无权限处理器
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        // 重定向到403页面
        this.redirectStrategy.sendRedirect(request, response, "/auth/accessError");
    }
}
  • WebSecurityConfig配置类配置无权限处理器
...前面代码省略...
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())  // 自定义用户未登录处理器
.accessDeniedHandler(new CustomAccessDeniedHandler())  // 自定义用户无权限处理器
...后面代码省略...
  • 启动项目访问测试
    访问首页,点击用户列表,跳转到403页面
    在这里插入图片描述
    在这里插入图片描述

html页面控制隐藏无权限接口

  • 引入maven依赖
   <!--   thymeleaf对spring security的集成     -->
    <dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    </dependency>
  • 首页index.html添加安全校验属性
    在这里插入图片描述
  • 再次重启访问首页时,则用户列表标签不会显示
    在这里插入图片描述

以上就是Spring Security的入门教程,本次教程的完整代码已上传至码云,欢迎大家测试
git clone https://gitee.com/xiaolin-v-java/spring-security-demo.git

参考来源

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值