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中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

SpringCloud动态配置注解@RefreshScope与@Component的深度解析

《SpringCloud动态配置注解@RefreshScope与@Component的深度解析》在现代微服务架构中,动态配置管理是一个关键需求,本文将为大家介绍SpringCloud中相关的注解@Re... 目录引言1. @RefreshScope 的作用与原理1.1 什么是 @RefreshScope1.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java中StopWatch的使用示例详解

《Java中StopWatch的使用示例详解》stopWatch是org.springframework.util包下的一个工具类,使用它可直观的输出代码执行耗时,以及执行时间百分比,这篇文章主要介绍... 目录stopWatch 是org.springframework.util 包下的一个工具类,使用它

Java进行文件格式校验的方案详解

《Java进行文件格式校验的方案详解》这篇文章主要为大家详细介绍了Java中进行文件格式校验的相关方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、背景异常现象原因排查用户的无心之过二、解决方案Magandroidic Number判断主流检测库对比Tika的使用区分zip