SpringSecurity-5-UserDetailsService与UserDetails接口(用户来源)

本文主要是介绍SpringSecurity-5-UserDetailsService与UserDetails接口(用户来源),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎你来看我的SpringSecurity系列文章,因为是系列文章,所以部分文章内容是有承前启后的关系

这个UserDetailsService接口是用户数据的来源,就是登录用户的数据从哪里来,是从数据库来?还是从你电脑的TXT文本上过来的,还是从其他地方,该接口会返回UserDetails接口的实例,而这个UserDetails接口,就是用户信息,因为用户信息除了username,password这些字段之外,不同项目会包含不同的信息,比如在我的项目中,用户登录之后会在用户信息中添加age,birthday等信息,所以spring定义了一个UserDetails接口,就是为了方便我们自定义字段的

任何实现了UserDetailsService接口的实现类,都可以作为认证数据的来源,只要把这个实现类的实例注入到spring容器中,SpringSecurity便可以自动发现并使用该实例

所以本篇文章将演示:用户shiwentian想访问test1,但是需要登录,用户输入帐号shiwentian,密码123456,通过UserDetailsService接口拿到该帐号的用户,匹配帐号密码,成功登陆,然后将用户的username,age,birthday等信息放到UserDetails接口实例,转换成json字符串,最终返回到前台页面,然后由前端发起请求访问test1,当然了,本文不会展示前端发送请求的代码

步骤1: 首先我们定义一个controller,里面包含一个方法

@RestController
public class AController {@RequestMapping("/test1")public String test1() {return "OK1";}
}

步骤2: 然后定义一个用户类,上文也说了,因为我们要返回给前端age和birthday,所以这个类必须我们自己实现UserDetails接口


public class TestUser implements UserDetails {// 注意,这个password是需要加密之后,再set到本类的,本文后续会有代码再次提示该注意事项private String password;// username是spring security默认的账号字段名字private String username;// 正是因为存在下面2个字段,所以我们需要自己实现UserDetails,spring自己提供了// 几个UserDetails接口的实现,但是我们要返回给前端age和birthday,所以不用spring的private int age;		private String birthday;@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}// 注意:下面3个方法如果返回false,则spring security会登录失败并且在错误信息中提示"用户已失效"// 错误信息在AuthenticationFailureHandler接口的AuthenticationException.getMessage()方法中// 错误信息文章请参考本系列文章的第4章节:AuthenticationFailureHandler接口@Overridepublic boolean isAccountNonExpired() {// 本文为了演示,恒定写死返回truereturn true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}// 如果此方法返回false,则spring security会登录失败并且在错误信息中提示"用户帐号已被锁定"// 错误信息在AuthenticationFailureHandler接口的AuthenticationException.getMessage()方法中// 错误信息文章请参考本系列文章的第4章节:AuthenticationFailureHandler接口@Overridepublic boolean isAccountNonLocked() {// 本文为了演示,恒定写死返回truereturn true;}// 省略了其他重写方法,省略了部分get/set方法,为了篇幅更短
}

步骤3: 接下来用户类有了,但是用户类里有一个比较特殊的字段,就是password,在spring security中,它要求我们先根据帐号,查询到这个用户,然后spring security自己拿着前端页面传递过来的密码进行一次加密,spring加密完前端密码之后,会用这个加密后的密码,和我们上述代码返回的TestUser类的password对比,所以这里遇到的问题就是:spring security需要知道加密方式是什么
spring是这样做的,如果容器中有PasswordEncoder接口实例,则直接使用该接口实例进行加密,如果容器中不存在PasswordEncoder接口实例,则系统直接报错,错误信息为:There is no PasswordEncoder mapped for the id "null",所以我们自己往容器中注入一个PasswordEncoder,代码如下:

@Service
public class TestPasswordEncoder extends BCryptPasswordEncoder {// 这种写法叫做Full Configuration Class
}

在这里我多说一下,上述中的TestPasswordEncoder 直接继承了spring自带的BCryptPasswordEncoder ,其实更简单的方法应该是下面这样的,在启动类或者某个@Service或者@Component等等这种类中使用@Bean注解

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}// 写这个就不用写上面的TestPasswordEncoder了// 这种写法叫做Lite Configuration Class@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

我为什么要自己写一个TestPasswordEncoder 呢?主要是我突然想到了full模式和lite模式,也没什么,就是复习一下这两个模式而已。。。你也可以参考这篇ConfigurationClassUtils类复习一下,主要就是lite模式适用于已有的类,但是没有注入到容器中,例如本文中的BCryptPasswordEncoder

步骤4: 接下来实现UserDetailsService接口,用于查询登录的帐号密码是否正确


@Service
public class TestUserDetailsService implements UserDetailsService {// 上文已经注入了PasswordEncoder,所以我们直接注入到本类,当然,在代码里new一个也行,因为加密规则都相同,无所谓@Resourceprivate PasswordEncoder encoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 如果输入的帐号是shientian,则模拟从数据库查询出该账号的密码if ("shiwentian".equals(username)) {TestUser user = new TestUser();user.setUsername("史文天");user.setPassword(encoder.encode("123456"));// 模拟从数据库查询出密码user.setAge(28);user.setBirthday("1996-03-12");return user;}// 如果输入的帐号不是shiwentian,则直接返回null,表示没有该账号,此时spring security会提示帐号密码不匹配return null;
}

步骤5: 帐号密码正确,登录成功,将该人员信息返回给前端页面

public class TestReturnJsonValueHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {// 主要注意一下getPrincipal方法,它返回的就是本文开头的TestUser类,也就是UserDetails接口的实现类Object testUser = authentication.getPrincipal();response.setContentType("application/json;charset=UTF-8");PrintWriter out=response.getWriter();ObjectMapper jsonObj = new ObjectMapper();String jsonString = jsonObj.writeValueAsString(testUser);out.write(jsonString);out.close();// authentication.getAuthorities();}
}

步骤6: 启动服务,浏览器输入http://localhost:8081/test1,此时会跳转到登录页面,输入帐号shiwentian,密码123456,则浏览器上显示该人员的Json字符串格式的信息

本文到此就结束了,在spring security中,有UserDetailsService接口是有几个实现类的,所以我们可以不自己实现,例如查询数据库可以直接使用JdbcUserDetailsManager,它就是一个UserDetailsService接口

下一篇文章:菜单接口权限

这篇关于SpringSecurity-5-UserDetailsService与UserDetails接口(用户来源)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057389

相关文章

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Spring MVC如何设置响应

《SpringMVC如何设置响应》本文介绍了如何在Spring框架中设置响应,并通过不同的注解返回静态页面、HTML片段和JSON数据,此外,还讲解了如何设置响应的状态码和Header... 目录1. 返回静态页面1.1 Spring 默认扫描路径1.2 @RestController2. 返回 html2

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java操作ElasticSearch的实例详解

《Java操作ElasticSearch的实例详解》Elasticsearch是一个分布式的搜索和分析引擎,广泛用于全文搜索、日志分析等场景,本文将介绍如何在Java应用中使用Elastics... 目录简介环境准备1. 安装 Elasticsearch2. 添加依赖连接 Elasticsearch1. 创

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

SpringBoot 整合 Grizzly的过程

《SpringBoot整合Grizzly的过程》Grizzly是一个高性能的、异步的、非阻塞的HTTP服务器框架,它可以与SpringBoot一起提供比传统的Tomcat或Jet... 目录为什么选择 Grizzly?Spring Boot + Grizzly 整合的优势添加依赖自定义 Grizzly 作为

Java后端接口中提取请求头中的Cookie和Token的方法

《Java后端接口中提取请求头中的Cookie和Token的方法》在现代Web开发中,HTTP请求头(Header)是客户端与服务器之间传递信息的重要方式之一,本文将详细介绍如何在Java后端(以Sp... 目录引言1. 背景1.1 什么是 HTTP 请求头?1.2 为什么需要提取请求头?2. 使用 Spr

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

java Stream操作转换方法

《javaStream操作转换方法》文章总结了Java8中流(Stream)API的多种常用方法,包括创建流、过滤、遍历、分组、排序、去重、查找、匹配、转换、归约、打印日志、最大最小值、统计、连接、... 目录流创建1、list 转 map2、filter()过滤3、foreach遍历4、groupingB