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 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll