安全框架SpringSecurity入门【详解,附有图文+示例代码】

十一.SpringSecurity入门

点击跳转熟☞:【安全框架SpringSecurity进阶【详解,附有图文+示例代码】】

11.0 介绍

Spring Security 是一个功能强大、可高度定制的认证和授权框架,专为保护基于 Spring 的应用程序而设计。它提供了多种安全机制,包括但不限于用户认证、授权、密码加密、防止 CSRF 攻击、跨站请求伪造攻击(XSS)、会话管理、OAuth、LDAP 集成等。

核心概念

  • 认证(Authentication):确认用户身份的过程,通常通过用户名和密码实现。Spring Security 会管理当前用户的身份信息(例如:Authentication 对象)。
  • 授权(Authorization):在认证后,判断用户是否有权访问某资源或执行某操作,通常根据角色、权限来进行控制。

Spring Security的常见功能

  • 基于表单的登录:通过用户名和密码登录应用。
  • 基于HTTP Basic的身份验证:通过 HTTP 请求头提供用户名和密码。
  • 基于角色的授权:通过用户的角色(如 ROLE_ADMIN, ROLE_USER)来控制访问权限。
  • 基于方法的授权:通过注解控制方法的访问权限,如 @PreAuthorize
  • 密码加密和存储:支持各种密码加密算法(如 BCrypt、PBKDF2、SHA等)。
  • 跨站请求伪造(CSRF)防护:Spring Security 内置了 CSRF 攻击防护功能。
  • 会话管理:支持并发会话控制、会话固定攻击防护等。

11.1 简单认识

首先导入依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>
              spring-boot-starter-security
            </artifactId>
</dependency>

controller

@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/hello01")
    public String hello(){
        return "hellokwh";
    }

}

在这里插入图片描述

默认用户名user

访问该地址,

http://localhost:8080/hello/hello01

在这里插入图片描述

可以修改用户名和密码:

  1. 在 application.properties 中进行配置
  2. 通过 Java 代码配置在内存中
  3. 通过 Java 从数据库中加载

配置文件配置用户名/密码

可以直接在 application.properties 文件中配置用户的基本信息:

spring.security.user.name=kwh
spring.security.user.password=123

11.2 Java代码配置在内存中

常规用户访问

创建配置类继承WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity // 开启SpringSecurity的功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        //从内存获取用户认证信息的服务类(了解)后期用户的信息要从表中获取
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //构建用户,真实开发中用户信息要从数据库加载构建
        UserDetails u1 = User
                .withUsername("itcast")
          //{noop}:no operration--》表示登录时对避免不做任何操作,
          //说白了就是明文比对
                .password("{noop}123456")
               .authorities("P5", "ROLE_ADMIN")//用户的权限信息
                .build();

        UserDetails u2 = User
                .withUsername("ithz")
                .password("{noop}123456")
         //如果角色也作为一种权限资源,则角色名称的前缀必须加ROLE_
                .authorities("P7", 
                             "ROLE_SELLER","ROLE_ADMIN")
                .build();

        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }

}

controller

@RestController
@RequestMapping("/hello")
public class HelloController {

    @RequestMapping("/hello01")
    public String hello(){
        return "hellokwh";
    }

}

再次访问

http://localhost:8080/hello/hello01

只有用户itcast和ithz用户才能登录

用户分配权限

编码方式

controller

@RestController
@ResponseBody
public class HelloController {

    //@PreAuthorize("hasAuthority('P5')")
    @GetMapping("/hello")
    public String hello(){
        return "hello world today";
    }

    //@PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/ss01")
    public String springSecurity01(){
        return "springSecurity01";
    }


   // @PermitAll
    // @PreAuthorize("permitAll()")
    @GetMapping("/register")
    public String springSecurity02(){
        return "register.....";
    }

}

配置类

@Configuration
@EnableWebSecurity // 开启SpringSecurity的功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        //从内存获取用户认证信息的服务类(了解)后期用户的信息要从表中获取
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //构建用户,真实开发中用户信息要从数据库加载构建
        UserDetails u1 = User
                .withUsername("itcast")
          //{noop}:no operration--》表示登录时对避免不做任何操作,说白了就是明文比对
                .password("{noop}123456")
                //用户的权限信息
                .authorities("P5", "ROLE_ADMIN")
                .build();

        UserDetails u2 = User
                .withUsername("ithz")
                .password("{noop}123456")
          //如果角色也作为一种权限资源,则角色名称的前缀必须加ROLE_
                .authorities("P7",
                             "ROLE_SELLER","ROLE_ADMIN")
                .build();

        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }


    // 配置资源权限绑定设置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // super.configure(http);

        http.formLogin()
                .and()
                .logout()//登出用默认的路径登出 /logout
                .permitAll()//允许所有的用户访问登录或者登出的路径
                .and()
                .csrf().disable()//启用CSRF,防止CSRF攻击
                .authorizeRequests()//授权方法,该方法后有若干子方法进行不同的授权规则处理
          //允许所有账户都可访问(不登录即可访问),同时可指定多个路径
                .antMatchers("/register").permitAll()//注册,允许所有人访问该路径
                .antMatchers("/hello").hasAuthority("P5")
                .antMatchers("/ss01").hasRole("ADMIN")//拥有ROLE_ADMIN角色的人才可以访问; 底层将role加ROLE_前缀转化成权限标识
                .anyRequest().authenticated();//除了上边配置的请求资源,其它资源都必须授权才能访问
    }
}
注解方式

controller

@RestController
@ResponseBody
public class HelloController {

    @PreAuthorize("hasAuthority('P5')")
    @GetMapping("/hello")
    public String hello(){
        return "hello world today";
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/ss01")
    public String springSecurity01(){
        return "springSecurity01";
    }


    @PermitAll
    // @PreAuthorize("permitAll()")
    @GetMapping("/register")
    public String springSecurity02(){
        return "register.....";
    }


}

【配置类】

@Configuration
@EnableWebSecurity // 开启SpringSecurity的功能
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启SpringSecurity注解支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        //从内存获取用户认证信息的服务类(了解)后期用户的信息要从表中获取
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //构建用户,真实开发中用户信息要从数据库加载构建
        UserDetails u1 = User
                .withUsername("itcast")
                .password("{noop}123456")//{noop}:no operration--》表示登录时对避免不做任何操作,说白了就是明文比对
               .authorities("P5", "ROLE_ADMIN")//用户的权限信息
                .build();

        UserDetails u2 = User
                .withUsername("ithz")
                .password("{noop}123456")
           //如果角色也作为一种权限资源,则角色名称的前缀必须加ROLE_
                .authorities("P7", 
                             "ROLE_SELLER","ROLE_ADMIN")
                .build();

        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }


    // 配置资源权限绑定设置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // super.configure(http);

        http.formLogin()
                .and()
                .logout()//登出用默认的路径登出 /logout
                .permitAll()//允许所有的用户访问登录或者登出的路径
                .and()
                .csrf().disable()//启用CSRF,防止CSRF攻击
                .authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理
                // //允许所有账户都可访问(不登录即可访问),同时可指定多个路径
                // .antMatchers("/register").permitAll()//注册,允许所有人访问该路径
                // .antMatchers("/hello").hasAuthority("P5")
                // .antMatchers("/ss01").hasRole("ADMIN")//拥有ROLE_ADMIN角色的人才可以访问; 底层将role加ROLE_前缀转化成权限标识
                // .anyRequest().authenticated();//除了上边配置的请求资源,其它资源都必须授权才能访问
    }
}

11.3 数据库中获取用户名和密码

第一种方式

首先在resources下的static包下创建登录表单login.html

<form action="/formlogin" method="post">
    用户名:
    <input type="text" name="username" ></br></input>
    密码:
    <input type="password" name="password" ></br></input>
    <input type="submit" value="登录">
</form>

配置文件:(用Mybatis-Plus)

server:
  port: 80

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
    username: root
    password: sa123456

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: false
  mapper-locations: classpath:mapper/*xml

【数据库】

DROP TABLE IF EXISTS `smbms_user`;
CREATE TABLE `smbms_user`  (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `userCode` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL DEFAULT NULL COMMENT '用户编码',
  `userName` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL DEFAULT NULL COMMENT '用户名称',
  `userPassword` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL DEFAULT NULL COMMENT '用户密码',
  `gender` int NULL DEFAULT NULL COMMENT '性别(1:女、 2:男)',
  `birthday` date NULL DEFAULT NULL COMMENT '出生日期',
  `phone` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL DEFAULT NULL COMMENT '手机',
  `address` varchar(30) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NULL DEFAULT NULL COMMENT '地址',
  `userRole` bigint NULL DEFAULT NULL COMMENT '用户角色(取自角色表-角色id)',
  `createdBy` bigint NULL DEFAULT NULL COMMENT '创建者(userId)',
  `creationDate` datetime NULL DEFAULT NULL COMMENT '创建时间',
  `modifyBy` bigint NULL DEFAULT NULL COMMENT '更新者(userId)',
  `modifyDate` datetime NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_unicode_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

【实体类】

@Data
@ToString
@TableName("smbms_user")
public class User {
    private Integer id; //id
    private String userCode; //用户编码
    private String userName; //用户名称
    private String userPassword; //用户密码
    private Integer gender;  //性别
    private Date birthday;  //出生日期
    private String phone;   //电话
    private String address; //地址
    private Integer userRole;    //用户角色
    private Integer createdBy;   //创建者
    private Date creationDate; //创建时间
    private Integer modifyBy;     //更新者
    private Date modifyDate;   //更新时间
    
}

【mapper】

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

创建LoginAuthenticationProvider类,实现接口AuthenticationProvider

// 自定义认证
@Component
public class LoginAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserMapper userMapper;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        // 获取表单提交的用户名和密码
        String userName = (String) authentication.getPrincipal(); //拿到username
        String password = (String) authentication.getCredentials(); //拿到password

        //调用数据库进行校验
        User user = userMapper.selectOne(new QueryWrapper<User>()
                .eq("userCode", userName)
                .eq("userPassword", password));

        // 登录成功
        if (user != null) {
            // 权限集合
            // 认证登录对象

            // UsernamePasswordAuthenticationToken roleUser = new UsernamePasswordAuthenticationToken(
            //         user,
            //         user.getUserPassword(),
            //         AuthorityUtils
            //                 .commaSeparatedStringToAuthorityList("ROLE_USER"));
            // return roleUser;

            // 权限集合
            List<GrantedAuthority> authorities = AuthorityUtils
                    .commaSeparatedStringToAuthorityList("ROLE_USER");

            UsernamePasswordAuthenticationToken roleUser =
                    new UsernamePasswordAuthenticationToken(
                    user,
                    user.getUserPassword(),
                    authorities);

            return roleUser;
        }

        // 登录失败返回 null
        return null;
    }



    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LoginAuthenticationProvider loginAuthenticationProvider;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // super.configure(auth);

        //自定义认证,替换认证的拦截器
   auth.authenticationProvider(loginAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // super.configure(http);

        // 关闭csrf防护
        http.csrf().disable()
          .headers()
          .frameOptions().disable();

       //认证配置
        http.formLogin()
                //登录页面
                .loginPage("/login.html")
          .loginProcessingUrl("/formlogin")//表单提交的路径
          .defaultSuccessUrl("/index/info")//登录成功跳转路径
          .usernameParameter("username")//表单中用户名字段
          .passwordParameter("password")//表单中密码字段
          .failureUrl("/loginout.html")//登录失败跳转路径
          .permitAll();//允许所有用户访问


        // 权限配置
        http.authorizeRequests()
                .antMatchers("/").permitAll()//允许所有用户访问
                .antMatchers("/index/**").hasAnyRole("USER","ADMIN")
                .antMatchers("/user/**").hasRole("USER")
          
                //除了上面的资源,其他请求需要认证
                .antMatchers("/admin/**").hasRole("ADMIN");
     
    }
}

controller

@RestController
@RequestMapping("/admin")
public class AdminController {

    @GetMapping("/info")
    public String adminInfo(){
        return "admin info。。。。。";
    }

}
============================================
  
@RestController
@RequestMapping("/index")
public class IndexController {

    @GetMapping("/info")
    public String index() {
        return "这是首页...";
    }

}
===================================================
@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/info")
    public String userinfo(){
        return "userInfo....";
    }

}

  

访问http://localhost:80/index/info,进入自己定义的页面login.html

第二种方式

【数据库】

create database security_demo default charset=utf8mb4;
use security_demo;

CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `roles` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `tb_user` VALUES (1, 'itcast', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_ADMIN,P5');
INSERT INTO `tb_user` VALUES (2, 'itheima', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_SELLER,P7,ROLE_ADMIN');

在这里插入图片描述

【pom.xml】

<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.1.4</version>
</dependency>

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

【application.properties】

server.port=8080

#spring.security.user.name=admin
#spring.security.user.password=admin

mybatis.mapper-locations=classpath:mapper/*xml

mybatis.type-aliases-package=com.ithz.pojo

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/security_demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull

spring.datasource.username=root
spring.datasource.password=sa123456

【实体类】

@Data
@NoArgsConstructor
@AllArgsConstructor
// @Builder
public class TbUser {
    private Integer id;

    private String username;

    private String password;

    private String roles;

    private static final long serialVersionUID = 1L;
}

【mapper】

@Mapper
public interface TbUserMapper {

    //根据用户名称查询用户信息
 TbUser findByUserName(@Param("userName") String userName);

}

【xml】

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ithz.mapper.TbUserMapper">

    <select id="findByUserName" resultType="com.ithz.pojo.TbUser">
        select  id,username,password,roles from tb_user where username = #{userName}
    </select>
</mapper>

【自定义UserDetailsService】

public interface MyUserDetailsService {

    //根据用户名称查询用户信息
  TbUser findByUserName(@Param("userName") String userName);

}
/**
 * 定义用户认证时获取认证用户详情信息服务的bean
 */
@Service
public class MyUserDetailsServiceImpl implements MyUserDetailsService, UserDetailsService {

    @Autowired
    private TbUserMapper tbUserMapper;

    @Override
    public TbUser findByUserName(String userName) {
        return null;
    }

    /**
     * 根据认证时传入的用户名去指定资源库下获取详情信息:用户名、密码密文、权限对象集合
     * @param username the username identifying the user whose data is required.
     * @return
     * @throws UsernameNotFoundException
     */
    // UserDetailsService
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        TbUser dbUser = tbUserMapper.findByUserName(username);

        if (dbUser == null) {
            throw new UsernameNotFoundException("当前用户不存在");
        }

        // 组装用户详情信息:用户名、密码密文、权限对象集合
        List<GrantedAuthority> authorities = AuthorityUtils
                .commaSeparatedStringToAuthorityList(dbUser.getRoles());

        UserDetails userDetails = User.builder()
                .username(dbUser.getUsername())// 用户名
                .password(dbUser.getPassword()) //密码密文
                .authorities(authorities)// 权限对象集合
                .build();
        return userDetails;
    }
}

【在配置类 SecurityConfig中配置Bean: 】

@Configuration
@EnableWebSecurity // 开启SpringSecurity的功能
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启SpringSecurity注解支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     * @return
     */
    // @Bean
    // public UserDetailsService userDetailsService(){
    //     //从内存获取用户认证信息的服务类(了解)后期用户的信息要从表中获取
    //     InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
    //     //构建用户,真实开发中用户信息要从数据库加载构建
    //     UserDetails u1 = User
    //             .withUsername("itcast")
    //             .password("{noop}123456")//{noop}:no operration--》表示登录时对避免不做任何操作,说白了就是明文比对
    //             .authorities("P5", "ROLE_ADMIN")//用户的权限信息
    //             .build();
    //
    //     UserDetails u2 = User
    //             .withUsername("ithz")
    //             .password("{noop}123456")
    //              //如果角色也作为一种权限资源,则角色名称的前缀必须加ROLE_
    //             .authorities("P7", "ROLE_SELLER","ROLE_ADMIN")
    //             .build();
    //
    //     inMemoryUserDetailsManager.createUser(u1);
    //     inMemoryUserDetailsManager.createUser(u2);
    //     return inMemoryUserDetailsManager;
    // }


    // 配置资源权限绑定设置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // super.configure(http);

        http.formLogin()
                .and()
                .logout()//登出用默认的路径登出 /logout
                .permitAll()//允许所有的用户访问登录或者登出的路径
                .and()
                .csrf().disable()//启用CSRF,防止CSRF攻击
                .authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理
                // //允许所有账户都可访问(不登录即可访问),同时可指定多个路径
                // .antMatchers("/register").permitAll()//注册,允许所有人访问该路径
                // .antMatchers("/hello").hasAuthority("P5")
                // .antMatchers("/ss01").hasRole("ADMIN")//拥有ROLE_ADMIN角色的人才可以访问; 底层将role加ROLE_前缀转化成权限标识
                // .anyRequest().authenticated();//除了上边配置的请求资源,其它资源都必须授权才能访问
    }


    /**
     * 配置密码加密方式
     * @return
     */
    @Bean
    public PasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }



}

注意:

1.User是UserDetails接口的实现类,封装了用户权限相关的的数据及用户的权限数据, 不要导错包 ;

2.工程已经配置好了BCryptPasswordEncoder加密相关bean,底层会自动调用;

【controller】

@RestController
@ResponseBody
public class HelloController {

    @PreAuthorize("hasAuthority('P5')")
    @GetMapping("/hello")
    public String hello(){
        return "hello world today";
    }

    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/ss01")
    public String springSecurity01(){
        return "springSecurity01";
    }

    @PermitAll
    // @PreAuthorize("permitAll()")
    @GetMapping("/register")
    public String springSecurity02(){
        return "register.....";
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔚一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值