学习SpringBoot使用SpringSecurity(一)_表单登录、角色认证以及SpringSecurity导致CSS样式丢失问题

一、 环境准备

用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
用户授权指的是验证某个用户是否有权限执行某个操作。
spring security的主要核心功能为 认证和授权,所有的权限架构也是基于这两个核心功能去实现的。
 “认证”,是为用户建立一个他所声明的主体。主题一般是指用户,设备或可以在你系统中执行动作的其他系统。
 “授权”指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由 身份验证过程建立了。

导入依赖

# SpringBoot版本
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>


 <!-- SpringSecurity 权限 -->
        <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>
        <!--SpringBootweb-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!-- Mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
         <!--JPA-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

创建数据库

一般权限控制有三层,即:用户<–>角色<–>权限,用户与角色是多对多,角色和权限也是多对多。
用户表、角色表以及一个中间表
建库SQL语句:

CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='用户表';
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表';


CREATE TABLE `sys_user_role` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `fk_role_id` (`role_id`),
  CONSTRAINT `fk_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户与角色关联表';

# 初始化数据
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', 'zs', '123');

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

注意:
1.用户的登陆认证是由Spring Security进行处理的,请求路径默认为/login,用户名字段默认为username,密码字段默认为password
2.这里的权限格式为ROLE_XXX,是Spring Security规定的。
在这里插入图片描述

准备页面

在resources跟目录下创建index.html和home.html两个页面,登录页面是index,登录成功以后会跳转home页面。
在这里插入图片描述
index.html页面我使用了Bootstrap样式。
Bootstrap中文网官网样式下载

Bootstrap中文网
在这里插入图片描述

index.html

<!DOCTYPE html>
<html lang="zh-CN">
<!--<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">-->
<head>
    <meta charset="utf-8">
    <meta name="description" content="SpringSecurityDemo">
    <meta name="author" content="LiuShihao">
    <title>SpringSecurityLogin</title>
<!--    不需要加上static 目录-->
    <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css">
    <link href="/css/signin.css" rel="stylesheet" type="text/css">
</head>

<body>
<div class="container">
    <form class="form-signin" action="/login" method="post">
        <h2 class="form-signin-heading">SpringSecurity</h2>
        <input type="text" name="username"  class="form-control" placeholder="Username" required autofocus>
        <br><input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
        <button class="btn btn-lg btn-primary btn-block" type="submit">登  录</button>
    </form>
</div>
</body>
</html>


home.html

<!DOCTYPE html>
<html lang="zh-CN">
<!--<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">-->
<head>
    <meta charset="utf-8">
    <meta name="description" content="练习使用SpringSecurity">
    <meta name="author" content="LiuShihao">
    <title>HOME</title>
    <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css">
    <link href="/css/signin.css" rel="stylesheet" type="text/css">
</head>

<body>
<div class="container">
    <h1>登录成功</h1>
    <a href="/admin">检测ROLE_ADMIN角色</a><hr><br>
    <a href="/user">检测ROLE_USER角色</a><hr><br>
    <button onclick="window.location.href='/logout'">退出登录</button>
</div>
</body>
</html>

yml配置文件

server:
  port: 9002
spring:
  application:
    name: SpringSecurityDemo
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://116.62.13.104:3306/tensquare_user?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF8
    username: root
    password: 123456
  jpa:       #  SpringDataJPA的配置
    database: mysql
    show-sql: true
  # springboot默认访问static,resources,public这些文件夹下的文件,而没有默认访问templates下的
  resources:
    static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,classpath:/templates/
  thymeleaf:
    cache: false

实体类

@Data
@Entity(name = "sys_role")
public class SysRole {
    @Id
    private Integer id;
    private String name;
}
@Data
@Entity(name = "sys_user")
public class SysUser {

    @Id
    private Integer id;
    private String name;
    private String password;
}
@Data
@Entity(name = "sys_user_role")
public class SysUserRole {
    @Id
    private Integer userId;
    private Integer roleId;
}

数据访问层

角色表

public interface SysRoleRepository extends JpaRepository<SysRole,Integer> {
    /**
     * 根据 角色id查询角色
     * @param id
     * @return
     */
    SysRole findSysRolesById (Integer id);

    /**
     * 根绝角色name 查询角色
     * @param name
     * @return
     */
    SysRole findSysRoleByName(String name);

}

用户表

public interface SysUserRepository extends JpaRepository<SysUser,Integer> {
    SysUser findSysUsersByName(String name);
}

用户角色中间表

public interface SysUserRoleRepository extends JpaRepository<SysUserRole,Integer> {
    List<SysUserRole>  findSysUserRolesByUserId(int userID);
}

业务层

public interface SysService {

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    SysUser findUserByUsername(String username);

    /**
     * 根据用户ID 查询角色信息ID
     * @param id
     * @return
     */
    List<SysUserRole> findRolesIdByUserID(Integer id);

    /**
     * 根据角色ID 查询角色
     * @param roleId
     * @return
     */
    SysRole findRoleByRoleId(Integer roleId);
}

业务层实现类

@Service
public class SysServiceImpl implements SysService {
    /**
     * 用户表
     */
    @Autowired
    SysUserRepository sysUserRepository;
    /**
     * 角色表
     */
    @Autowired
    SysRoleRepository sysRoleRepository;

    /**
     * 用户角色中间表
     */
    @Autowired
    SysUserRoleRepository sysUserRoleRepository;



    /**
     * 根据用户名查询用户信息   查询用户表
     * @param username
     * @return
     */
    @Override
    public SysUser findUserByUsername(String username) {
        SysUser sysUser = sysUserRepository.findSysUsersByName(username);
        return sysUser;
    }

    /**
     * 根据用户ID查询角色ID   查询用户角色中间表
     * @param id
     * @return
     */
    @Override
    public List<SysUserRole> findRolesIdByUserID(Integer id) {
        List<SysUserRole> sysUserRolesByUserId = sysUserRoleRepository.findSysUserRolesByUserId(id);
        return sysUserRolesByUserId;
    }

    /**
     * 根据角色ID查询角色信息   查询角色表
     * @param roleId
     * @return
     */
    @Override
    public SysRole findRoleByRoleId(Integer roleId) {
        return sysRoleRepository.findSysRolesById(roleId);
    }

    /**
     * 根据角色Name查询角色信息   查询角色表
     * @param roleName
     * @return
     */
    @Override
    public SysRole findRoleIdBuRoleName(String roleName) {
        return sysRoleRepository.findSysRoleByName(roleName);
    }

二、UserDetailsService

UserDetailsService是连接我们的数据库和springsecurity的桥梁。
首先我们需要自定义 UserDetailsService ,将用户信息和权限注入进来。

我们需要重写 loadUserByUsername 方法,参数是用户输入的用户名。返回值是UserDetails,这是一个接口,一般使用它的子类org.springframework.security.core.userdetails.User,它有三个参数,分别是用户名、密码和权限集。
代码逻辑:
根据登陆页面传入的username查询用户信息,如果存在,则根据用户ID查询角色ID(用户角色中间表),(返回的是集合,因为一个用户可能对应多个角色,是一对多的关系),再遍历集合,根据角色ID查询角色信息,最后返回用户名、密码和权限集。

package com.lsh.service.Impl;

import com.lsh.model.SysRole;
import com.lsh.model.SysUser;
import com.lsh.model.SysUserRole;
import com.lsh.service.SysService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author :LiuShihao
 * @date :Created in 2020/11/9 3:09 下午
 * @desc :自定义的进行认证的具体实现类
 *  * 根据用户名查出对应的角色,将角色信息交给SpringSecurity托管
 *  * 在访问对应资源时就会自动去验证该用户是否有权限访问
 */
// 注入时按名字注入
@Service("userDetailsService")
@Slf4j
public class MyUserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    SysService sysService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据 username 查询数据库  用户信息
        SysUser user = sysService.findUserByUsername(username);
        log.info("用户:"+user);
        // 判断用户是否存在
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        // 存在用户   然后根据用户ID查询所有角色信息Id
        List<SysUserRole> rolesId = sysService.findRolesIdByUserID(user.getId());
        // 根据从[用户角色中间表]中查出的角色信息(也就是角色id),
        // 去角色表中查询全部角色名
        // 并将其角色信息放入GrantedAuthority集合
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for (SysUserRole sysUserRole : rolesId) {
           SysRole role =  sysService.findRoleByRoleId(sysUserRole.getRoleId());
           log.info("角色:"+role);
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        /**
         * 返回值是UserDetails,这是一个接口,
         * 一般使用它的子类org.springframework.security.core.userdetails.User,
         * 它有三个参数,分别是用户名、密码和权限集
         */
        return new User(user.getName(), user.getPassword(), authorities);
    }
}

三、SpringSecurityConfig

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

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

package com.lsh.config;

import com.lsh.service.Impl.MyUserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author :LiuShihao
 * @date :Created in 2020/11/3 10:45 上午
 * @desc :
 */
// 配置类注解
@Configuration
// 开启 Security 服务
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     *  注入自定义认证类
     */
    @Autowired
    @Qualifier("userDetailsService")
    private MyUserDetailsServiceImpl userDetailsService;

    /**
     * 密码的转码解码
     * @param auth
     * @throws Exception
     */
    @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());
            }
        });
    }
    /**
     * 定义具体的路径资源对应的权限
     *  authorizeRequests所有security全注解配置实现的开端,表示开始说明需要的权限。
     *  需要的权限分两部分,第一部分是拦截的路径,第二部分访问该路径需要的权限。
     *  antMatchers表示拦截什么路径,permitAll任何权限都可以访问,直接放行所有。
     *  anyRequest()任何的请求,authenticated认证后才能访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                // 如果有允许匿名的url,填在下面
                .antMatchers("/anon").permitAll()
                //  /admin资源需要ROLE_ADMIN角色才能访问
                .antMatchers("/admin").hasRole("ADMIN")
                //  /user 资源需要有ROLE_USER才能访问
                .antMatchers("/user").hasRole("USER")
                //释放静态资源
                .antMatchers("/js/**","/css/**","/images/*","/fonts/**","/**/*.png","/**/*.jpg").permitAll()
                // 所有请求都需要认证后才能访问
                .anyRequest().authenticated()
                .and()
                // 设置登陆页
                .formLogin().loginPage("/login")
                // 设置登陆成功页
                .defaultSuccessUrl("/").permitAll()
                // 自定义登陆用户名和密码参数,默认为username和password
//                .usernameParameter("username")
//                .passwordParameter("password")
                .and()
                .logout().permitAll();

                // 关闭CSRF跨域
                http.csrf().disable();
    }
    /**
     * 设置拦截忽略文件夹,可以对静态资源放行  与上边的一样 用那个都可以:
     * @param web
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 忽略URL
        web.ignoring().antMatchers("/**/*.js", "/image/*.jepg", "/**/*.css", "/**/*.js", "/**/*.map", "/**/*.html",
                "/**/*.png");
    }

}

Bug CSS样式丢失

在这里插入图片描述
原因:样式引用路径写错或者css被SpringSecurity拦截器拦截。
解决方法:

第一:引用路径写错。
在这里插入图片描述
第二:在SpringSecurityConfig释放样式
在这里插入图片描述

// 配置类注解
@Configuration
// 开启 Security 服务
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     *  注入自定义认证类
     */
    @Autowired
    @Qualifier("userDetailsService")
    private MyUserDetailsServiceImpl userDetailsService;

    /**
     * 密码的转码解码
     * @param auth
     * @throws Exception
     */
    @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());
            }
        });
    }
    /**
     * 定义具体的路径资源对应的权限
     *  authorizeRequests所有security全注解配置实现的开端,表示开始说明需要的权限。
     *  需要的权限分两部分,第一部分是拦截的路径,第二部分访问该路径需要的权限。
     *  antMatchers表示拦截什么路径,permitAll任何权限都可以访问,直接放行所有。
     *  anyRequest()任何的请求,authenticated认证后才能访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                // 如果有允许匿名的url,填在下面
                .antMatchers("/anon").permitAll()
                //  /admin资源需要ROLE_ADMIN角色才能访问
                .antMatchers("/admin").hasRole("ADMIN")
                //  /user 资源需要有ROLE_USER才能访问
                .antMatchers("/user").hasRole("USER")
                //释放静态资源
                         .antMatchers("/js/**","/css/**","/images/*","/fonts/**","/**/*.png","/**/*.jpg").permitAll()
                // 所有请求都需要认证后才能访问
                .anyRequest().authenticated()
                .and()
                // 设置登陆页
                .formLogin().loginPage("/login")
                // 设置登陆成功页
                .defaultSuccessUrl("/").permitAll()
                // 自定义登陆用户名和密码参数,默认为username和password
//                .usernameParameter("username")
//                .passwordParameter("password")
                .and()
                .logout().permitAll();

                // 关闭CSRF跨域
                http.csrf().disable();
    }
    /**
     * 设置拦截忽略文件夹,可以对静态资源放行  与上边的一样 用那个都可以:
     * @param web
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 忽略URL
        web.ignoring().antMatchers("/**/*.js", "/image/*.jepg", "/**/*.css", "/**/*.js", "/**/*.map", "/**/*.html",
                "/**/*.png");
    }
}

在这里插入图片描述

在这里插入图片描述
代码已上传码源仓库:Gitee仓库地址

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 您好,关于您的问题,我可以为您提供一些思路和建议。 首先,您可以使用 layui 发送基于 Ajax 的请求,向后端服务器发送登录请求。在发送请求之前,需要先获取用户输入的用户名和密码等登录信息。 其次,在发送登录请求时,需要将登录信息进行编码和加密,以保证登录信息的安全性。具体的编码和加密方式可以根据实际需要进行选择。 最后,后端服务器需要进行身份认证和权限控制,确保用户登录信息的有效性和合法性。在这个过程中,可以使用 Spring Security 来进行身份认证和授权管理,以保证系统的安全性和可靠性。 总之,使用 layui 发送 Spring Security登录请求需要结合前端和后端的技术,同时需要对用户登录信息进行编码和加密,以确保系统的安全性和可靠性。 ### 回答2: Layui是一款轻量级的前端UI框架,而Spring Security 是一个功能强大的基于Spring框架的安全管理框架。要使用Layui发送Spring Security登录请求,可以按照以下步骤进行操作: 1. 引入Layui相关资源文件:在HTML页面中引入Layui的CSS和JS文件,确保页面正确加载Layui框架。 2. 创建登录表单使用Layui的表单组件,创建一个登录表单,其中包括用户名和密码的输入框,以及一个发送登录请求的按钮。 3. 编写JavaScript代码:使用Layui的JavaScript API,监听登录按钮的点击事件,获取用户输入的用户名和密码,然后通过Ajax方式将数据以JSON格式发送到后端。 4. 后端处理登录请求:在后端使用Spring Security框架,配置登录接口的URL和相应的权限控制等。在接收到登录请求后,对用户名和密码进行验证,验证通过后返回相应的登录成功提示信息。 5. 前端处理登录结果:前端在接收到后端返回的登录结果后,可以根据返回的信息,使用Layui的弹层组件,展示登录成功或登录失败的提示,并进行相应的跳转或处理。 总结起来,使用Layui发送Spring Security登录请求主要包括引入相关资源文件、创建登录表单、编写JavaScript代码、后端处理登录请求和前端处理登录结果等步骤。需要注意的是,Spring Security的配置和功能较为复杂,需要熟悉相关文档和进行适当的配置才能实现登录功能的完整实现。 ### 回答3: 为了使用Layui发送Spring Security登录请求,你可以按照以下步骤进行操作: 1. 引入Layui库和Spring Security库:首先,确保你的项目中已经引入了Layui和Spring Security的相关库文件。 2. 创建登录页面:使用Layui的表单组件创建登录页面。你可以使用Layui提供的表单元素,如文本框、密码框和提交按钮等。确保表单的提交方式是POST,并且表单的action属性指向Spring Security登录验证URL。 3. 编写Spring Security配置:在Spring Security的配置类中,配置登录相关的URL、用户名、密码等信息。确保登录URL与前端Layui页面的action属性值一致。 4. 处理登录请求:在Spring Security的配置类中,编写处理登录请求的方法。该方法接收前端传来的用户名和密码参数,并对其进行验证。验证成功后,可以进行用户权限的加载等操作。 5. 响应登录结果:在Spring Security的配置类中,配置登录成功或失败后的响应。可以根据验证的结果,返回对应的页面或信息给前端。 6. 进行Layui和Spring Security的整合:在Layui的登录页面中,添加点击提交按钮时触发的事件。在事件中获取用户输入的用户名和密码,并将其通过Ajax方式发送到Spring Security登录URL。 7. 处理响应结果:根据Spring Security返回的响应结果,对登录成功或失败进行相应的处理。你可以使用Layui的弹窗组件,来显示登录成功或失败的提示信息。 总结:使用Layui发送Spring Security登录请求,主要是通过前后端的协作,实现用户信息的传递和验证。前端使用Layui库创建登录页面和发起登录请求,后端使用Spring Security库配置验证的相关信息和处理登录请求的逻辑。通过这种方式,可以实现安全的用户登录功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Liu_Shihao

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

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

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

打赏作者

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

抵扣说明:

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

余额充值