SpringSecurity入门案例

一、导入依赖

导入spring-boot-starter-security依赖,在SpringBoot2.x环境下默认使用的是5.0版本

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

二、创建数据库

一般权限控制有三层,即:用户<—>角色<—>权限,用户与角色是多对多,角色和权限也是多对多。这里占时不考虑权限,只考虑用户<—>角色
创建角色表tb_role

DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

创建用户表tb_user

DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

创建用户-角色关联表tb_user_role

DROP TABLE IF EXISTS `tb_user_role`;
CREATE TABLE `tb_user_role`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `user_id` int(0) NOT NULL,
  `role_id` int(0) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `user_id`(`user_id`) USING BTREE,
  INDEX `role_id`(`role_id`) USING BTREE,
  CONSTRAINT `tb_user_role_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `tb_user_role_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `tb_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

初始化一下数据:

INSERT INTO `tb_role` VALUES (1, 'ROLE_ADMIN');
INSERT INTO `tb_role` VALUES (2, 'ROLE_USER');

INSERT INTO `tb_user` VALUES (1, 'admin', '123');
INSERT INTO `tb_user` VALUES (2, 'user', '123');

INSERT INTO `tb_user_role` VALUES (1, 1, 1);
INSERT INTO `tb_user_role` VALUES (2, 2, 2);

角色名格式为ROLE_XXX,Spring Security规定的,不要乱起名字

三、准备页面

因为是案例程序,页面不需要太复杂,越简单越好,一个用于登录的login.html以及登录成功之后的index.html,将其放到resources/template目录下

  1. login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<form action="/login" method="post">
    用户名:<input type="text" name="username"><br/>
    密码:<input type="text" name="password"><br/>
    <button>登录</button>
</form>
</body>
</html>

用户的登录认证是由Spring Security进行处理的,请求路径默认为/login,用户名字段默认为username,密码字段默认为password

  1. index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录成功</h1>
<a href="/admin">检验ROLE_ADMIN角色</a>
<a href="/user">检验ROLE_USER角色</a>
<button onclick="window.location='/logout'">退出登录</button>
</body>
</html>

四、配置application.yml

配置一下数据连接即可:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true

五、创建实体、Dao、Service和Controller

5.1 实体
  1. User
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer id;
    private String name;
    private String password;
}
  1. Role
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer id;
    private String name;
}
  1. UserRole
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserRole implements Serializable {

    private static final long serialVersionUID = 1L;

    private Integer userId;
    private Integer roleId;
}

5.2 Dao
  1. UserMapper
public interface UserMapper {

    /**
     * 根据用户ID查询用户信息
     * @param id
     * @return
     */
    @Select("select * from tb_user where id = #{id}")
    User selectById(Integer id);

    /**
     * 根据用户查询用户信息
     * @param name
     * @return
     */
    @Select("select * from tb_user where name = #{name}")
    User selectByName(String name);
}
  1. RoleMapper
public interface RoleMapper {

    /**
     * 根据角色ID查询角色信息
     * @param id
     * @return
     */
    @Select("select * from tb_role where id = #{id}")
    Role selectById(Integer id);
}
  1. UserRoleMapper
public interface UserRoleMapper {

    /**
     * 根据用户ID查询角色列表信息
     * @param userId
     * @return
     */
    @Select("select * from tb_user_role where user_id = #{userId}")
    List<UserRole> listByUserId(Integer userId);
}
5.3 Service
  1. UserService
public interface UserService {

    /**
     * 根据用户ID查询用户信息
     * @param id
     * @return
     */
    User selectById(Integer id);

    /**
     * 根据用户名查询用户信息
     * @param name
     * @return
     */

    User selectByName(String name);
}
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public User selectById(Integer id) {
        return userMapper.selectById(id);
    }

    @Override
    public User selectByName(String name) {
        return userMapper.selectByName(name);
    }
}
  1. RoleService
public interface RoleService {

    /**
     * 根据角色ID查询角色信息
     * @param id
     * @return
     */
    Role selectById(Integer id);
}
@Service
public class RoleServiceImpl implements RoleService {

    @Resource
    private RoleMapper roleMapper;

    @Override
    public Role selectById(Integer id) {
        return roleMapper.selectById(id);
    }
}
  1. UserRoleMapper
public interface UserRoleService {

    /**
     * 根据用户ID查询角色列表信息
     * @param userId
     * @return
     */
    List<UserRole> listByUserId(Integer userId);
}
@Service
public class UserRoleServiceImpl implements UserRoleService {

    @Resource
    private UserRoleMapper userRoleMapper;

    @Override
    public List<UserRole> listByUserId(Integer userId) {
        return userRoleMapper.listByUserId(userId);
    }
}
5.4 Controller
@Slf4j
@Controller
public class LoginController {

    @GetMapping("/")
    public String show(){
        String name = SecurityContextHolder.getContext().getAuthentication().getName();
        log.info("当前登录用户:"+name);
        return "index";
    }

    @GetMapping("/login")
    public String login(){
        return "login";
    }

    @ResponseBody
    @GetMapping("/admin")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    public String printAdmin(){
        return "看到这句话,说明你有ROLE_ADMIN角色";
    }

    @ResponseBody
    @GetMapping("/user")
    @PreAuthorize("hasRole('ROLE_USER')")
    public String printUser(){
        return "看到这句话,说明你有ROLE_USER角色";
    }
}
  • 获取当前登录用户:SecurityContextHolder.getContext().getAuthentication().getName()
  • @PreAuthorize判断用户是否具有指定角色或权限才能访问,没有就不能访问

六、配置Spring Security

6.1 UserDetailsService

首先我们需要自定义 UserDetailsService,将用户信息和权限注入进来我们需要重写 loadUserByUsername 方法,参数是用户输入的用户名。返回值是UserDetails,这是一个接口,一般使用它的子类org.springframework.security.core.userdetails.User,它有三个参数,分别是用户名、密码和权限集。

实际情况下,大多将 DAO 中的 User 类继承 org.springframework.security.core.userdetails.User 返回。

@Service("userDetailsService")
public class UserDetailServiceImpl implements UserDetailsService {

    @Resource
    private UserService userService;

    @Resource
    private RoleService roleService;

    @Resource
    private UserRoleService userRoleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        //从数据库中查询用户信息
        User user = userService.selectByName(username);
        if (user == null){
            throw new UsernameNotFoundException("此用户不存在");
        }
        //添加权限
        List<UserRole> userRoles = userRoleService.listByUserId(user.getId());
        userRoles.forEach(userRole -> {
            Role role = roleService.selectById(userRole.getRoleId());
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        });
        return new org.springframework.security.core.userdetails.User(user.getName(),user.getPassword(),authorities);
    }
}
6.2 WebSecurityConfig

该类是 Spring Security 的配置类,该类的三个注解分别是标识该类是配置类、开启 Security 服务、开启全局 Securtiy 注解。

首先将我们自定义的 userDetailsService 注入进来,在 configure() 方法中使用 auth.userDetailsService() 方法替换掉默认的 userDetailsService

这里我们还指定了密码的加密方式(5.0 版本强制要求设置),因为我们数据库是明文存储的,所以明文返回即可,如下所示:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return s.equals(charSequence.toString());
            }
        });
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**","/js/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage("/login")
                .defaultSuccessUrl("/")
                .permitAll()
                .and()
                .logout().permitAll()
                .and()
                .csrf().disable();
    }
}

在这里插入图片描述

七、运行程序

ROLE_ADMIN 用户:用户名 admin,密码 123
ROLE_USER用户:用户名user,密码 123

以admin用户进行登录

登录失败
在这里插入图片描述
这是拥有ROLE_ADMIN角色正常显示
在这里插入图片描述
未拥有ROLE_USER角色,报错异常403
在这里插入图片描述
注:如果想将密码加密,可以修改configure()方法如下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     auth.userDetailsService(userDetailsService)
     		.passwordEncoder(new BCryptPasswordEncoder());
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值