Spring Boot 整合 Spring Security

Posted 晨M风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot 整合 Spring Security相关的知识,希望对你有一定的参考价值。

1.建库

  a.创建 用户表、角色表、关系表

CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `sys_user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL
);

 

  b.初始化数据

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

INSERT INTO `sys_user` VALUES (1, admin, 123);
INSERT INTO `sys_user` VALUES (2, test, 123);

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

COMMIT;

 

 

2.创建springboot工程

  a.选择Spring Web、JDBC API、MyBatis Framework、mysql Driver、Spring Security、Thymeleaf,并手动添加 thymeleaf-extras-springsecurity5 依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--对Thymeleaf添加Spring Security标签支持-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
    </dependencies>

 

 

  b.编写 application.yml 配置

spring:
  # 服务名称
  application:
    name: springboot-security
  # mysql数据源
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
    username: root
    password: 123456
  # thymeleaf热加载
  thymeleaf:
    cache=false: false

# 服务端口号
server:
  port: 8081

#开启Mybatis下划线命名转驼峰命名
mybatis:
  configuration:
    map-underscore-to-camel-case: true

 

 

  c.创建Entity实体类

    1)用户Entity

public class SysUser implements Serializable {

    //ID
    private Integer id;
    //用户名
    private String name;
    //密码
    private String password;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

 

    2)角色Entity

public class SysRole implements Serializable {

    //ID
    private Integer id;
    //角色名称
    private String name;

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

 

    3)用户-角色关系Entity

public class SysUserRole implements Serializable {

    //用户ID
    private Integer userId;
    //角色ID
    private Integer roleId;

    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public Integer getRoleId() {
        return roleId;
    }
    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }
}

 

 

  d.创建 Mybatis 的 Mapper

    1)用户Mapper

@Mapper
public interface SysUserMapper {

    @Select("SELECT * FROM sys_user WHERE id = #{id}")
    SysUser selectById(Integer id);

    @Select("SELECT * FROM sys_user WHERE name = #{name}")
    SysUser selectByName(String name);
}

 

    2)角色Mapper

@Mapper
public interface SysRoleMapper {

    @Select("SELECT * FROM sys_role WHERE id = #{id}")
    SysRole selectById(Integer id);

}

 

    3)用户-角色关系Mapper

@Mapper
public interface SysUserRoleMapper {

    @Select("SELECT * FROM sys_user_role WHERE user_id = #{userId}")
    List<SysUserRole> listByUserId(Integer userId);

}

 

 

  e.创建Service

    1)用户Service

@Service
public class SysUserService {

    @Resource
    private SysUserMapper userMapper;

    public SysUser selectById(Integer id) {
        return userMapper.selectById(id);
    }

    public SysUser selectByName(String name) {
        return userMapper.selectByName(name);
    }

}

 

    2)角色Service

@Service
public class SysRoleService {

    @Resource
    private SysRoleMapper roleMapper;

    public SysRole selectById(Integer id){
        return roleMapper.selectById(id);
    }

}

 

    3)用户-角色Service

@Service
public class SysUserRoleService {

    @Resource
    private SysUserRoleMapper userRoleMapper;

    public List<SysUserRole> listByUserId(Integer userId) {
        return userRoleMapper.listByUserId(userId);
    }

}

 

 

  f.创建Controller

@Controller
public class LoginController {

    private Logger logger = LoggerFactory.getLogger(LoginController.class);

    @RequestMapping("/")
    public String showHome() {
        String name = SecurityContextHolder.getContext().getAuthentication().getName();
        logger.info("当前登陆用户:" + name);

        return "home.html";
    }

    @RequestMapping("/login")
    public String showLogin() {
        return "login.html";
    }

    @RequestMapping("/admin")
    @ResponseBody
    @PreAuthorize("hasRole(‘ADMIN‘)")
    public String printAdmin() {
        return "如果你看见这句话,说明你有ROLE_ADMIN角色";
    }

    @RequestMapping("/user")
    @ResponseBody
    @PreAuthorize("hasRole(‘USER‘)")
    public String printUser() {
        return "如果你看见这句话,说明你有ROLE_USER角色";
    }

}

 

 

  g.配置SpringSecurity

    1)创建 Service 实现 UserDetailsService,将用户信息和权限注入进来

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Resource
    private SysUserService userService;

    @Resource
    private SysRoleService roleService;

    @Resource
    private SysUserRoleService userRoleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        // 从数据库中取出用户信息
        SysUser user = userService.selectByName(username);

        // 判断用户是否存在
        if(user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }

        // 添加权限
        List<SysUserRole> userRoles = userRoleService.listByUserId(user.getId());
        for (SysUserRole userRole : userRoles) {
            SysRole role = roleService.selectById(userRole.getRoleId());
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }

        // 返回UserDetails实现类
        return new User(user.getName(), user.getPassword(), authorities);
    }

}

 

     2)创建 配置类 继承 WebSecurityConfigurerAdapter

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

    @Resource
    private CustomUserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
//                return DigestUtils.md5DigestAsHex((charSequence.toString() + "salt").getBytes());
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
//                return s.equals(DigestUtils.md5DigestAsHex((charSequence.toString() + "salt").getBytes()));
                return s.equals(charSequence.toString());
            }
        });
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/", "/login").permitAll()//允许匿名访问的url
//                .antMatchers("/user/**").hasRole("USER")//允许指定角色访问的url
//                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()//其他所有url都需要验证
                .and()
                .exceptionHandling().accessDeniedPage("/403.html")// 权限不足自动跳转403
                .and()

                // 设置登陆页
                .formLogin().loginPage("/login")
                .failureUrl("/login?error")// 登陆失败页面(参数可供前端显示弹框)
//                .usernameParameter("username")// 自定义登陆用户名,默认为username
//                .passwordParameter("password")//自定义登陆密码参数,默认为password
                .and()

                //登出
                .logout()
                .logoutSuccessUrl("/");

        // 关闭CSRF跨域
        http.csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) {
        // 设置拦截忽略文件夹,可以对静态资源放行
        web.ignoring().antMatchers("/css/**", "/js/**");
    }

}

    注:@Configuration 表示此类是个配置类,@EnableWebSecurity 表示开启SpringSecurity服务,@EnableGlobalMethodSecurity 表示开启SpringSecurity全局注解

 

 

  h.创建 Html 文件

    1)templates 文件夹中创建 login.html 和 home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <h1>登陆</h1>
    <form method="post" action="/login">
        <div>
            <label>用户名:</label><input type="text" name="username">
        </div>
        <div>
            <label>密码:</label><input type="password" name="password">
        </div>
        <div>
            <button type="submit">立即登陆</button>
        </div>
    </form>
</body>
</html>

 

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <!--匿名-->
    <div sec:authorize="isAnonymous()">
        未登录,点击 <a th:href="@{/login}">登录</a>
    </div>

    <!--登录-->
    <div sec:authorize="isAuthenticated()">
        <h1>登陆成功</h1>
        <p>用户名: <span sec:authentication="principal.username"></span></p>
        <p>权限: <span sec:authentication="principal.authorities"></span></p>
        <p>
            <span sec:authorize="hasRole(‘ADMIN‘)">这段话ADMIN能看到</span>
            <span sec:authorize="hasRole(‘USER‘)">这段话USER能看到</span>
        </p>
        <a href="/admin">检测ADMIN角色</a>
        <a href="/user">检测USER角色</a>
        <button onclick="location.href=‘/logout‘">退出登录</button>
    </div>
</body>
</html>

 

     2)在 static 文件夹中创建 403.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>权限不足</title>
</head>
<body>
    <h1>权限不足</h1>
</body>
</html>

 

 

3.启动服务登录 http://localhost:8081 测试效果

 

4.注意

  a.数据库中角色名称务必加上 “ROLE_” 前缀,authorities.add(new SimpleGrantedAuthority(role.getName())) 需要 “ROLE_”打头的的名称

  b.hasRole(‘ADMIN‘) 需要去掉 “ROLE_” 前缀,而 hasAuthority("ROLE_ADMIN") 则需要加上“ROLE_” 前缀,即 hasRole(‘ADMIN‘)  与 hasAuthority("ROLE_ADMIN")  效果相同

 

5.参考文档

  https://blog.csdn.net/yuanlaijike/article/details/80249235

  https://www.jianshu.com/p/dcf227d53ab5

 

以上是关于Spring Boot 整合 Spring Security的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot如何整合Redis

Spring Boot如何整合Redis

Spring Boot:Spring Boot整合FreeMarker

spring boot 系列之四:spring boot 整合JPA

Spring Boot系列Spring Boot整合持久层

Spring Boot 2.X - Spring Boot整合AMQP之RabbitMQ