我是java spring的新手,我正在尝试通过开发自己的应用程序来练习我的技能。现在我正在尝试使用jwt令牌创建授权。我写了它的loginc,并遇到了一个小问题。我不知道为什么,但我试图验证的主要用户总是空。
逻辑是这样的:
1.首先,这是我创建的所有实体:UserEntity
@Entity
@Table(name = "users")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String email;
private String login;
private String password;
private String name;
private String surname;
@ManyToOne
@JoinColumn(name="schools_id")
private SchoolEntity school;
@ManyToOne
@JoinColumn(name="classes_id")
private ClassEntity userClass;
@ManyToMany(fetch = FetchType.EAGER)
private List<RoleEntity> roles = new ArrayList<>();
public UserEntity() {
}
public List<RoleEntity> getRoles() {
return roles;
}
public void setRoles(List<RoleEntity> roles) {
this.roles = roles;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public long getId() {
return id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public SchoolEntity getSchool() {
return school;
}
public void setSchool(SchoolEntity school) {
this.school = school;
}
public ClassEntity getUserClass() {
return userClass;
}
public void setUserClass(ClassEntity userClass) {
this.userClass = userClass;
}
}
RoleEntity
个
@Entity
@Table(name = "roles")
public class RoleEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
public RoleEntity() {
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SchoolEntity
@Entity
@Table(name = "schools")
public class SchoolEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
@OneToMany(mappedBy = "school")
private List<UserEntity> user;
public SchoolEntity() {
}
public long getId() {
return id;
}
public List<UserEntity> getUser() {
return user;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ClassEntity
@Entity
@Table(name = "classes")
public class ClassEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int number;
private String letter;
@OneToMany(mappedBy = "userClass")
private List<UserEntity> students;
public ClassEntity() {
}
public long getId() {
return id;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getLetter() {
return letter;
}
public void setLetter(String letter) {
this.letter = letter;
}
public List<UserEntity> getStudents() {
return students;
}
public void setStudents(List<UserEntity> students) {
this.students = students;
}
}
1.然后,我将UserDetailsService
的实现添加到我的UserService
类中,在这里我覆盖了loadUserByUsername
,它通过 login 字段查找用户:
Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepo;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity user = userRepo.findByLogin(username);
if(user == null) {
throw new UsernameNotFoundException("user with such username doesn't exists");
}
ArrayList<SimpleGrantedAuthority> userRoles = new ArrayList<>();
for(var role: user.getRoles()) {
userRoles.add(new SimpleGrantedAuthority(role.getName()));
}
User u = new User(user.getLogin(), user.getPassword(), userRoles);
return u;
}
在屏幕截图中,UserDetails
对象是如何创建的:
1.在此之后,我创建了身份验证过滤器来生成jwt令牌并对用户进行身份验证:
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public AuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = request.getParameter("username");
String password = request.getParameter("password");
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(usernamePasswordAuthenticationToken);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authentication) throws IOException, ServletException {
UserDetails userPrincipal = (User) request.getUserPrincipal();
Algorithm algorithm = JWTConfig.getAlgorithm();
String accessToken = JWT.create()
.withSubject(userPrincipal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 30 * 1000 * 60))
.withClaim("roles", userPrincipal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
String refreshToken = JWT.create()
.withSubject(userPrincipal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 60 * 1000 * 60))
.sign(algorithm);
Map<String, String> tokens = new HashMap<>();
tokens.put("access_token", accessToken);
tokens.put("refresh_token", refreshToken);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);
}
}
1.我还创建了授权过滤器。
public class AuthorizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if(header != null && header.startsWith("Bearer ")) {
try {
String token = header.substring("Bearer ".length());
JWTVerifier jwtVerifier = JWT.require(JWTConfig.getAlgorithm()).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String login = decodedJWT.getSubject();
List<SimpleGrantedAuthority> roles = new ArrayList<>();
for (var role : decodedJWT.getClaim("roles").asArray(String.class)) {
roles.add(new SimpleGrantedAuthority(role));
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(login, null, roles);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
filterChain.doFilter(request, response);
} catch (Exception e) {
response.setHeader("error", e.getMessage());
response.sendError(403);
}
}
else {
filterChain.doFilter(request, response);
}
}
}
1.完成所有这些之后,我设置了SecurityConfig
:
@Configuration @EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationFilter customAuthenticationFilter = new AuthenticationFilter(authenticationManagerBean());
customAuthenticationFilter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.authorizeRequests().antMatchers("/api/login/**").permitAll();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers(HttpMethod.POST, "/api/users/").permitAll();
http.addFilter(customAuthenticationFilter);
http.addFilterBefore(new AuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
但是当我启动程序时,它告诉我在AuthenticationFilter
中得到的用户主体是null。这是抛出异常的行:
UserDetails userPrincipal = (User) request.getUserPrincipal();
所以如果你知道,有什么问题,请告诉我。我真的很感激!
1条答案
按热度按时间pxy2qtax1#
成功验证后,Spring Security将
Authentication
对象设置为SecurityContextHolder
,这样你就可以从Authentication
中获取你的username
,注入到successfulAuthentication()
方法中--只需将你的代码更改为:您在那里得到
null
的原因是Spring只使用SecurityContextHolderAwareRequestWrapper
将Principal
对象设置为SecurityContextHolderAwareRequestFilter
中的HttpServletRequest
,并且该过滤器是在**UsernamePasswordAuthenticationFilter
之后**调用的。因此,在您的自定义
UsernamePasswordAuthenticationFilter
实现中,Spring还没有将Principal
对象设置为HttpServletRequest
。Spring Security过滤器链的顺序可以在here in the official reference documentation中找到。