草庐IT

springboot获取登录用户的个人信息

江湖侠客 2024-01-10 原文

在Spring Boot中,获取登录用户的个人信息通常需要使用Spring Security框架来进行身份认证和授权。Spring Security提供了一个名为SecurityContextHolder的上下文对象,它包含了当前请求的身份认证信息。通过SecurityContextHolder,可以访问当前已认证的用户的信息。

1.Pojo实体类

当使用 Spring Boot + Spring Security 构建 Web 应用程序时,我们需要定义用户实体类来存储用户信息。以下是一个基本的 User 实体类

@Entity
@Table(name = "users")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String username;
    
    @Column(nullable = false)
    private String password;
    
    @Column(nullable = false)
    private String fullName;
    
    @Column(nullable = false, unique = true)
    private String email;
    
    // 用户角色定义为一个字符串,用逗号分隔
    @Column(nullable = false)
    private String roles;
    
    // 其他字段和方法
    
    // UserDetails 方法的实现
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.stream(roles.split(","))
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

在这个例子中,User 实体类使用 @Entity @Table 注解进行了标记,表明它是一个 JPA 实体类,并且对应了一个名为 users 的数据库表。实体类中包含了一些基本的属性,例如 idusernamepasswordfullName email。另外,roles 属性定义了用户的角色,这是一个字符串,多个角色之间用逗号分隔。

User 实体类实现了 UserDetails 接口,该接口包含了一些必须实现的方法,用于获取用户的授权信息。在 getAuthorities() 方法中,我们将用户的角色字符串分割成多个角色,并将其转换为 Spring Security 中的 GrantedAuthority 对象。其余的方法返回的都是 true,表示这些限制条件都没有被启用。

你还可以根据你的业务需求对 User 实体类进行扩展或修改。

要获取已认证用户的信息,可以在控制器方法中注入Authentication对象,然后从该对象中获取用户信息。例如:

2. Controller层

import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/me")
    public UserDetails getUserDetails(Authentication authentication) {
        return (UserDetails) authentication.getPrincipal();
    }
}

在上面的代码中,使用@GetMapping注解将控制器方法映射到“/me”路径。方法中注入Authentication对象,并从该对象中获取Principal,即已认证用户的信息。由于Spring Security默认使用UserDetailsService来加载用户信息,因此Principal通常是UserDetails对象。

如果您想要访问更多的用户信息,可以自定义一个UserDetailsService,通过UserDetailsService从数据库或其他数据源中加载用户信息。然后,您可以通过从Principal中获取用户名或其他标识符来检索用户信息。

当使用Spring Security进行身份认证时,需要实现UserDetailsService接口来加载用户信息。UserDetailsService接口只有一个方法:loadUserByUsername,该方法返回一个实现了UserDetails接口的对象,该对象包含有关用户的信息。

下面是一个简单的UserDetailsService类的示例,它从内存中的用户列表中加载用户信息:

3. Service层

import java.util.ArrayList;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class MyUserDetailsService implements UserDetailsService {

    private List<User> users = new ArrayList<>();

    public MyUserDetailsService() {
        users.add(new User("user1", "password1", getAuthorityList("ROLE_USER")));
        users.add(new User("user2", "password2", getAuthorityList("ROLE_USER")));
        users.add(new User("admin", "password", getAuthorityList("ROLE_USER", "ROLE_ADMIN")));
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        for (User user : users) {
            if (user.getUsername().equals(username)) {
                return user;
            }
        }
        throw new UsernameNotFoundException("User not found with username: " + username);
    }

    private List<GrantedAuthority> getAuthorityList(String... roles) {
        List<GrantedAuthority> authorityList = new ArrayList<>();
        for (String role : roles) {
            authorityList.add(new SimpleGrantedAuthority(role));
        }
        return authorityList;
    }
}

在上面的代码中,MyUserDetailsService类实现了UserDetailsService接口,并重写了loadUserByUsername方法。该方法首先检查用户列表中是否存在与传递的用户名匹配的用户,如果找到该用户,则将其作为UserDetails对象返回。否则,抛出UsernameNotFoundException异常。

4. Spring Security配置类

在这个例子中,用户列表是硬编码在MyUserDetailsService类中的。在实际应用中,您将从数据库或其他数据源中获取用户信息,并在loadUserByUsername方法中使用它。此外,您还可以根据需要添加更多的用户属性,例如电子邮件地址、电话号码等。

最后,值得一提的是,在实现UserDetailsService接口时,请务必确保密码已被加密并保存为哈希值。在本示例中,密码是明文存储的,但在实际应用中,应使用Spring Security提供的密码编码器来对密码进行加密。

以下是一份完整的示例代码,包括一个自定义的UserDetailsService和Spring Security配置类:

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasAnyRole("ADMIN", "USER")
                .antMatchers("/").permitAll()
                .and().formLogin()
                .and().logout().logoutSuccessUrl("/login").permitAll();
    }

}

class MyUserDetailsService implements UserDetailsService {

    private List<User> users = new ArrayList<>();

    public MyUserDetailsService() {
        users.add(new User("user1", "$2a$10$HKV7WJB/FNH8/3wXXtGB0.bDrxPlheBlsHSDkOizQ94RyL5v5n5oy", getAuthorityList("ROLE_USER")));
        users.add(new User("user2", "$2a$10$L9.Jir1vH6Wjtr8ZnI6FseV6rHdLWnV7yI0g1lV7vLmFFopW8VzU6", getAuthorityList("ROLE_USER")));
        users.add(new User("admin", "$2a$10$5aJN5O5pyrgQMDR.Ta5/0.hxOeSw/3nm3q9XcVvBIzDdtyA8DPZ3C", getAuthorityList("ROLE_USER", "ROLE_ADMIN")));
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        for (User user : users) {
            if (user.getUsername().equals(username)) {
                return user;
            }
        }
        throw new UsernameNotFoundException("User not found with username: " + username);
    }

    private List<GrantedAuthority> getAuthorityList(String... roles) {
        List<GrantedAuthority> authorityList = new ArrayList<>();
        for (String role : roles) {
            authorityList.add(new SimpleGrantedAuthority(role));
        }
        return authorityList;
    }

}

上面的代码包含了一个自定义的UserDetailsService实现类和一个Spring Security配置类。以下是代码中的一些要点:

5. 测试注意事项

MyUserDetailsService类:这是一个自定义的UserDetailsService实现类,用于从硬编码的用户列表中加载用户信息。在loadUserByUsername方法中,根据传递的用户名

以下是使用Postman测试API接口的步骤:

  1. 打开Postman软件并创建一个新的请求。在请求的URL地址栏中输入API接口的URL,例如http://localhost:8080/user/info

  2. 点击请求方式旁边的下拉菜单,选择HTTP请求方法,例如GET方法。

  3. 在请求头(Headers)中添加必要的认证信息,例如添加一个Authorization头,其值为Bearer加上访问令牌(access token),例如Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMSIsInJvbGUiOiJVU0VSIiwiaWF0IjoxNTE2MjM5MDIyfQ.kNqr5ZCw5vFh1dW8Kq3t_ZL-3q_kh12VTWg_w8mZisI

  4. 如果API接口需要传递请求参数,可以在请求参数(Params)中添加参数名和参数值。

  5. 点击发送按钮,发送请求并查看响应结果。

  6. 如果请求成功,可以在响应结果中看到API接口返回的数据,如果请求失败,可以查看响应状态码和错误信息来定位问题。

需要注意的是,在测试API接口时,需要了解API接口的请求参数、请求方式、认证方式等信息,并在请求头和请求参数中正确传递信息。另外,为了避免误操作,建议在测试环境中测试API接口,不要在生产环境中测试API接口

根据上面提供的代码,如果在Postman中成功请求 /user/info 接口,将会收到类似下面的 JSON 格式响应数据:

{
    "id": 1,
    "username": "user1",
    "fullName": "User One",
    "email": "user1@example.com"
}

其中,id 是用户ID,username 是用户名,fullName 是用户的全名,email 是用户的电子邮件地址。这些数据是在 UserInfo 类中定义的。

需要注意的是,如果没有授权访问该接口或者用户不存在,那么响应数据中将不会包含这些信息。如果出现这种情况,响应数据中可能会包含错误消息

6. Controller层代码改造

当使用 Spring Boot + Spring Security 构建 Web 应用程序时,可以使用 ResponseEntity 类来设置响应的 HTTP 状态码。ResponseEntity 类包装了 HTTP 响应的正文、HTTP 状态码以及响应头信息。它允许我们返回自定义响应实体,而不是使用默认的响应实体。

在你的代码中,你可以使用 ResponseEntity<UserInfo> 替换原本的 UserInfo 返回类型。这样,就可以将 HTTP 状态码作为 ResponseEntity 的一部分返回到客户端。在 getUserInfo() 方法中,你可以使用以下代码返回带有状态码的响应:

@GetMapping("/user/info")
public ResponseEntity<UserInfo> getUserInfo() {
    // 获取当前登录用户
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null || !authentication.isAuthenticated()) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }
    // 获取用户信息并返回
    String username = authentication.getName();
    UserInfo userInfo = userService.getUserInfoByUsername(username);
    return ResponseEntity.ok(userInfo);
}

在这个例子中,如果用户未授权或未经身份验证,则将返回 HTTP 401 状态码。如果用户已经经过身份验证,将返回 HTTP 200 状态码,并带有用户信息。

如果需要在 Postman 中测试 API 的响应状态码,可以在请求完成后查看响应面板的右上角,显示的是响应的状态码和消息。

在我上面已经回答针对如何使用 ResponseEntity 设置 HTTP 状态码做了说明,同时也提到了如何在 Postman 中查看响应的状态码。

在 Spring Boot 应用程序中,我们可以使用 ResponseEntity 类来包装响应实体,并指定返回的 HTTP 状态码。例如,在 getUserInfo() 方法中,我们可以使用以下代码返回带有

@GetMapping("/user/info")
public ResponseEntity<UserInfo> getUserInfo() {
    // 获取当前登录用户
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null || !authentication.isAuthenticated()) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }
    // 获取用户信息并返回
    String username = authentication.getName();
    UserInfo userInfo = userService.getUserInfoByUsername(username);
    return ResponseEntity.ok(userInfo);
}

在这个例子中,如果用户未授权或未经身份验证,则将返回 HTTP 401 状态码。如果用户已经经过身份验证,将返回 HTTP 200 状态码,并带有用户信息。

在 Postman 中查看响应的状态码,可以在请求完成后查看响应面板的右上角,显示的是响应的状态码和消息。如果返回的状态码为200,则表示请求成功,其他状态码则表示请求失败。

7. sql脚本

CREATE TABLE `users` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(50) NOT NULL,
  `password` VARCHAR(100) NOT NULL,
  `full_name` VARCHAR(100) NOT NULL,
  `email` VARCHAR(100) NOT NULL,
  `roles` VARCHAR(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_username` (`username`),
  UNIQUE KEY `UK_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

在这个例子中,我们定义了一个名为 users 的表,它包含了以下字段:

  • id:用户的唯一标识符,使用自增长的方式生成。
  • username:用户的登录名,必须是唯一的。
  • password:用户的登录密码,存储加密后的密码。
  • full_name:用户的全名,用于显示用户的真实姓名。
  • email:用户的电子邮件地址,必须是唯一的。
  • roles:用户的角色,以逗号分隔多个角色。

在实际应用中,你可以根据需要对数据表进行修改和扩展。

有关springboot获取登录用户的个人信息的更多相关文章

  1. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  4. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  5. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  6. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  7. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  8. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  9. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  10. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

随机推荐