赞
踩
水着水着我们就水到了比较重要的地方。
1前面我们分析认证的时候就会发现他在DaoAuthenticationProvider
中就已经有从数据库中获取用户名和密码的。
- public interface UserDetailsService {
- UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
- }
- 复制代码
意思是说我们只要重写这个类,就可以从我们所想要的地方获取用户信息。
那我们重写吧
- public class MybatisUserDetailsService implements UserDetailsService, UserDetailsPasswordService {
-
- @Resource
- private UsersMapper usersMapper;
-
- @Override
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
- Users users = usersMapper.loadUserByUsername(username);
- if (null == users) {
- throw new UsernameNotFoundException(username);
- }
- return users;
- }
-
- @Override
- public UserDetails updatePassword(UserDetails user, String newPassword) {
- if (user instanceof Users users) {
- users.setPassword(newPassword);
- usersMapper.updateByPrimaryKeySelective(users);
- }
- return user;
- }
- }
- 复制代码
发现这里需要把spring security的users
对象转换成我们所需要的users
对象。比较麻烦。
小白: "为什么不直接使用spring security那里面的users对象呢?"
小黑: "我们自己实现users对象的话就可以在自定义。特别是权限这边,我们肯定是需要自定义users对象的,不能直接使用其内部的对象。"
然后我们直接继承UserDetails
接口,然后自定义一些我们所需要的属性。说白了就是从内置users
对象中拷贝一些代码出来。
小黑: "这里我故意留下了一个坑,如果你真的从内置的
users
对象里复制代码的话,你会发现一个问题。"
- @Data
- @Builder
- @AllArgsConstructor
- @NoArgsConstructor
- public class Users implements Serializable, UserDetails {
- private Long id;
-
- private String username;
-
- private String password;
-
- private Boolean enabled;
-
- private Boolean accountnonexpired;
-
- private Boolean accountnonlocked;
-
- private Boolean credentialsnonexpired;
-
- private List<Roles> rolesList;
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public Collection<? extends GrantedAuthority> getAuthorities() {
- ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
- for (Roles roles : getRolesList()) {
- authorities.add(new SimpleGrantedAuthority(roles.getRole()));
- }
- return authorities;
- }
-
- @Override
- public boolean isAccountNonExpired() {
- return accountnonexpired;
- }
-
- @Override
- public boolean isAccountNonLocked() {
- return accountnonlocked;
- }
-
- @Override
- public boolean isCredentialsNonExpired() {
- return credentialsnonexpired;
- }
-
- @Override
- public boolean isEnabled() {
- return enabled;
- }
- }
- 复制代码
小白: "代码定义好了,但现在你要怎么加入到spring security中呢?让spring security主动调用我们自定义的类呢?"
小黑: "我们要使用自定义的类的话,无非是在
DaoAuthenticationProvider
里面设置。所以我们只要找到setUserDetailsService
这个函数就行了。看一下有哪些方法调用了这个函数?"
看了一下发现这边调用了setUserDetailsService
方法的。前面的UserDetailsService是对象是通过spring bean上下文面拿出来的。
那我们也可以效仿他的方式,在spring bean上添加我们的UserDetailsService
Bean。
- @Bean
- public UserDetailsService userDetailsService() {
- return new MybatisUserDetailsService();
- }
- 复制代码
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
- return httpSecurity.authorizeHttpRequests()
- .anyRequest().authenticated()
- .and()
- .formLogin()
- .defaultSuccessUrl("/hello", true) // 认证成功后访问
- .permitAll() // 白名单
- .and()
- .logout()
- .logoutUrl("/logout")
- .logoutSuccessUrl("/login") // 注销成功后访问
- .clearAuthentication(true)
- .invalidateHttpSession(true)
- .and()
- .build();
- }
- 复制代码
- @GetMapping("hello")
- public HashMap<String, Object> hello(Authentication authentication) {
- HashMap<String, Object> map = new HashMap<>();
- Object principal = authentication.getPrincipal();
- String name = authentication.getName();
- map.put("principal", principal);
- map.put("name", name);
- return map;
- }
- 复制代码
小白: "停一下, 你数据库表结构呢? "
小黑: "我忘了, 我找找在哪可以抄"
在分析UserDetailsService
的时候, 我们一般都要看看他的类族是怎样的, 结果发现可以偷懒的地方, 也就是表结构的位置
- create table users(username varchar_ignorecase(50) not null primary key,password varchar_ignorecase(500) not null,enabled boolean not null);
- create table authorities (username varchar_ignorecase(50) not null,authority varchar_ignorecase(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
- create unique index ix_auth_username on authorities (username,authority);
- 复制代码
这段
sql
要改改, 否则mysql
无法执行
结果发现, 就这...
我还不如自己设计呢
- SET NAMES utf8mb4;
- SET FOREIGN_KEY_CHECKS = 0;
-
- -- ----------------------------
- -- Table structure for authorities
- -- ----------------------------
- DROP TABLE IF EXISTS `authorities`;
- CREATE TABLE `authorities` (
- `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
- `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
- UNIQUE INDEX `ix_auth_username`(`username` ASC, `authority` ASC) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-
- -- ----------------------------
- -- Table structure for users
- -- ----------------------------
- DROP TABLE IF EXISTS `users`;
- CREATE TABLE `users` (
- `id` bigint NOT NULL AUTO_INCREMENT,
- `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
- `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
- `enabled` bit(1) NULL DEFAULT NULL,
- `accountNonExpired` bit(1) NULL DEFAULT b'1',
- `accountNonLocked` bit(1) NULL DEFAULT b'1',
- `credentialsNonExpired` bit(1) NULL DEFAULT b'1',
- PRIMARY KEY (`id`) USING BTREE
- ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-
- SET FOREIGN_KEY_CHECKS = 1;
- 复制代码
注意, 这里仅仅只是为了玩耍, 而非企业, 在企业中, 肯定不是这么设计的, 一般根据
RBAC
设计
小白: "这样就完成了?"
小黑: "完成了, 其他代码都是
mybatis
生成的, 简单"
启动, 访问 崩了
等下, spring security脱敏呢? 为什么这里可以输出密码?
分析源码看看
通过源码分析,脱敏应该是认证成功之后的事情
大概看了下源码, ProviderManager
有脱敏, 但是为什么不生效呢?
看这代码的意思, 是要我们在 Users
类上多添加一个接口CredentialsContainer
- public class Users implements Serializable, UserDetails, CredentialsContainer {
- @Override
- public void eraseCredentials() {
- // 设置 password 为 null
- this.password = null;
- }
- }
- 复制代码
行, 前面的坑补上了。再试试
完美~~~
记住UserDetailsService
怎么自定义, 注意自定义的User
需要实现哪些接口, 还有脱敏问题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。