springboot整合SpringSecurity并实现简单权限控制
创始人
2024-02-21 14:04:41
0

目录

一、SpringSecurity介绍

        案例效果:

二、环境准备

        2.1 数据库

        2.2 项目准备

三、确保项目没问题后开始使用

3.1、Security的过滤链:

3.2、自定义用户名密码登录:

方式1:将用户名密码写在配置文件里

方式2:使用数据库中的用户名、密码进行登录:

        第一步:新建一个类CustomerUserDetails实现UserDetails接口

        第二步:新建CustomerUserDetailsServiceImpl来实现UserDetailService接口

        第三步:配置类中注入bean对象:

3.3、自定义登录:

        第一步:自定义登录页面

        第二步:定义一个登录接口

        第三步:放行登录接口、请求登录接口

        第四步:在Service层使用ProviderManager的authenticate()方法进行验证

        实现效果:

过程中的一些报错:

认证过程:

                


一、SpringSecurity介绍

  SpringSecurity顾名思义是spring的一个安全框架。拥有认证和授权两大核心功能。

        案例效果:

二、环境准备

        2.1 数据库

RBAC模型:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。

即一个用户属于多种角色、一个角色有多个权限

 主体(subject) 访问资源的时候、通常由分为两种:基于角色控制访问、基于权限控制访问;

故建立五张表:用户表、权限表、角色表、用户角色表、角色权限表;

准备数据:

        张三--->管理员、普通用户------>增删改查 

       李四---->普通用户----->查询

脚本参考文章末尾的传送门

        2.2 项目准备

       使用mybatis作为持久层、且为了简化开发使用了mybatis-plus、本文着种于集成SpringSecurity,对整合mybatis-plus感兴趣的小伙伴可前往传送门浏览:

springboot整合mybatis-plus

       2.3 项目信息             

jdk 17 

springboot 2.7.0         

maven 3.8.6      

mysql 8.0.30

 导入必要jar包:主要导入:boot-security的整合依赖,其他根据需要导入

        org.springframework.bootspring-boot-starter-webmysqlmysql-connector-javacom.baomidoumybatis-plus-boot-starter3.4.0org.apache.velocityvelocity-engine-core2.3com.baomidoumybatis-plus-generator3.4.0org.springframework.bootspring-boot-starter-securityorg.springframework.bootspring-boot-starter-thymeleaforg.springframework.bootspring-boot-starter-testtestorg.projectlomboklomboktrue

配置文件:application.yml

spring:mvc:pathmatch:matching-strategy: ant_path_matcher
server:port: 8080
---
spring:datasource:url: jdbc:mysql://localhost:3308/boot_security?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&AllowPublicKeyRetrieval=Trueusername: rootpassword: root---
mybatis-plus:mapper-locations: mapper/*.xmlconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true

随后使用代码生成工具生成项目结构、随后调整xml文件位置、以及适当删减、随后测试下生成的代码是否可用。主要看有对应三个实体类就可。

    @Autowiredprivate TbUserService userService;@Testpublic void test01(){userService.list();}

静态资源准备:

三、确保项目没问题后开始使用

导入security整合依赖后、启动项目访问任何接口,都会被直接被直接拦截,并转发到security提供的登录页面、也就是需要认证一下才能进入首页。默认的用户名是user、密码在控制台。

        

 认证通过后才会访问到目标页面

3.1、Security的过滤链:

目前使用的是Security给的默认用户名和生成的密码。 实际情况是使用tb_user获取真实的用户名和密码;在此之前先了解Security的过滤链;

List filterList = context.getBean(DefaultSecurityFilterChain.class).getFilters();

SpringSecurity的过滤链:一共有16个过滤器链

 过滤器链的大概流程就是,用户请求过来、先检查用户名密码、没有错、则检查权限,若有对应权限、访问对应的接口、其中只要一步错,就给打回去;

3.2、自定义用户名密码登录:

方式1:将用户名密码写在配置文件里

spring:security:user:name: zspassword: 123

方式2:使用数据库中的用户名、密码进行登录:

        第一步:新建一个类CustomerUserDetails实现UserDetails接口

实现所有UserDetails的抽象方法并将TbUser【登录对象】 作为CustomerUserDetails的属性。

        

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerUserDetails implements UserDetails {private TbUser user;@Overridepublic Collection getAuthorities() {return null;}@Overridepublic String getPassword() {return user.getPassWord();}@Overridepublic String getUsername() {return user.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

        第二步:新建CustomerUserDetailsServiceImpl来实现UserDetailService接口

        实现loadUserByUsername方法。

/*** @author Alex*/
@Service
public class CustomerUserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate TbUserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名查询用户信息LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();wrapper.eq(TbUser::getUserName,username);TbUser user = userMapper.selectOne(wrapper);//如果查询不到数据就通过抛出异常来给出提示if(Objects.isNull(user)){throw new RuntimeException("用户名错误");}//封装成UserDetails对象返回return new CustomerUserDetails(user);}
}

此时由于数据库中的密码是明文,登录时会报一个错。

There is no PasswordEncoder mapped for the id "null"

因为没有给密码加密:

此时要想继续登录

                方式1【不推荐】:将数据库中明文前加{noop}即可

                方式2:使用Security默认的加密的工具类BCryptPasswordEncoder将密码加密后存入数据库。再SecurityConfig配置类中注入BCryptPasswordEncoder的bean对象即可。加密方式会自动加盐;

        第三步:配置类中注入bean对象:

/*** @author Alex*/
@Configuration
public class SecurityConfig{@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}}

    将密码字符串加密,调用encode()将密码加密。将加密后的字符串存入数据库;

    @Testpublic void testPasswordEncoder1(){String encode = securityConfig.passwordEncoder().encode("123");System.err.println(encode);}@Testpublic void testPasswordEncoder(){BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();String encode = passwordEncoder.encode("123");System.err.println(encode);}

         当注入bean对象后,明文前加{noop}就不可用了。

     简单提一下解密:

SpringSecurity提供了matches()方法来进行密码匹配,加密本身时不可逆的,解密的原理是将需要解密的字段统过相同的Hash函数得到的字符串到已加密的数据库中进行匹配。

3.3、自定义登录/认证:

        第一步:自定义登录页面

        第二步:定义一个登录接口

    /*** 登录方法、登录成功跳转到首先、* 否则继续跳转登录页,并给出提示* @param username* @return*/@PostMapping("/login")public Map userLogin(String username, String password){TbUser loginUser = new TbUser();loginUser.setUserName(username);loginUser.setPassWord(password);return userService.userLogin(loginUser);}

        第三步:放行登录接口、请求登录接口

    @Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable().authorizeRequests()// 允许匿名访问的接口.antMatchers("/user/login").anonymous().antMatchers("/toLogin").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();http.formLogin()// 访问登录页面接口.loginPage("/toLogin")// 执行登录方法接口.loginProcessingUrl("user/login");return http.build();}

        第四步:在Service层使用ProviderManager的authenticate()方法进行验证

将封装的Authentication对象 存入SecurityContextHolder

    @Overridepublic Map userLogin(TbUser loginUser, HttpSession session) {Map responseMap = new HashMap<>(2);try {UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginUser.getUserName(), loginUser.getPassWord(),null);Authentication authenticate = authenticationManager.authenticate(token);//   存入SecurityContextHolderSecurityContextHolder.getContext().setAuthentication(authenticate);responseMap.put("code","0");// 存入部分数据到session,方便区分用户、实际情况种可以省略CustomerUserDetails userDetails = (CustomerUserDetails) authenticate.getPrincipal();session.setAttribute("userName",userDetails.getUser().getUserName());return responseMap;}catch (RuntimeException e){responseMap.put("code","-1");e.printStackTrace();return responseMap;}}

此处直接返回map,也可以封装一个返回结果集对象,然后对于Security的 Session Management相关的内容会在后续文章中更新。

 前端监听表单提交后发送登录请求:

        //登录请求const url = "user/login";$.post(url,data,function(response){console.log(response.code);if(response.code==0){layer.msg("登录成功",{icon:6,time:1000}, function () {window.location = '/';});}else {layer.msg("用户名或密码错误",{icon:5,anim:6});$("#btn-login").removeAttr("disabled", "disabled").removeClass("layui-btn-disabled");}});

到这里呢、自定义登录就完成了、看下登录后的跳转的首页

        实现效果:

3.3、后端接口授权

        

3.4、前端按钮隐藏

过程中的一些报错

1、Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.

解决:配置文件加上

debug: true

2、No qualifying bean of type 'com.example.demo.mapper.TbUserMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

解决:启动类上加上mapperScan("com......")

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

认证过程:

问:springSecurity如何校验用户名、密码和权限?

答:通过一个登录请求的debug得到如下流程
        发送一个登录请求----->UsernamePasswordAuthenticationFilter--->通过authenticate()方法认证----->loadUserUsername()方法获得UserDetails对象-->从该对象中拿到密码对比系统中的密码---->给UserDetails对象添加权限并设置到Authentication中,存入SecurityContentHolder中即可。  (完成一个cookie--session的闭环)

                

  传送门:

springboot整合thymeleaf

springboot整合mybatis

springboot整合mybatis-plus

springboot整合shiro实现简单权限控制

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...