我是Java Spring的新手,我正试图通过开发我自己的应用程序来练习我的技能.现在,我正在try 使用JWT令牌创建授权.我写下了它的日志(log),并与这个小问题作了斗争.我不知道为什么,但我try 验证的主要用户总是空的.

所以逻辑是这样的:

  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对象是如何创建的:

enter image description here

  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();
        算法rithm algorithm = JWTConfig.get算法rithm();
        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.get算法rithm()).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中获得的用户主体为空.以下是引发异常的行:

UserDetails userPrincipal = (User) request.getUserPrincipal();

所以,如果你知道,有什么问题,请告诉我.我真的会感激它的!

推荐答案

身份验证成功后,Spring Security将Authentication对象设置为SecurityContextHolder,这样您就可以从Authentication中获得username,在successfulAuthentication()方法中注入-只需将代码更改为:

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        算法rithm algorithm = JWTConfig.get算法rithm();
        String accessToken = JWT.create()
                .withSubject(authentication.getName()) // <- getting name here
                .withExpiresAt(new Date(System.currentTimeMillis() + 30 * 1000 * 60))
                .withClaim("roles", authentication.getAuthorities().stream() // <- getting authorities here
                        .map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
                .sign(algorithm);
        String refreshToken = JWT.create()
                .withSubject(authentication.getName()) // <- and name again here
                .withExpiresAt(new Date(System.currentTimeMillis() + 60 * 1000 * 60))
                .sign(algorithm);
        // other code
    }

The reason you get null there is that Spring only sets Principal object to a HttpServletRequest in SecurityContextHolderAwareRequestFilter using SecurityContextHolderAwareRequestWrapper, and that filter is invoked after UsernamePasswordAuthenticationFilter.
So in your custom UsernamePasswordAuthenticationFilter implementation Spring has not set Principal object to the HttpServletRequest yet.

春泉安全过滤器连锁订单可以找到here in the official reference documentation个.

Java相关问答推荐

转换为Biggram

Java 8中的多个字段和计数

使用java访问具体子类特定方法的最佳方法是什么?

使用@MappdSuperClass扩展ParentClass&Won t继承ParentClass属性

为什么JAVA&S清洁器使用链表而不是并发HashSet?

SpringBoot+Java 17@Valid未验证POJO

Spring data JPA/Hibernate根据id获取一个列值

暂停计时器

将响应转换为带值的键

用OSQL创建索引

为什么我的回收视图会显示重复的列表?

在向WebSphere中的文档添加元素时iText挂起

在VS代码中,如何启用Java Main函数的&Q;Run|DEBUG&Q;代码?

错误:不兼容的类型:Double不能转换为Float

如何在Java springboot中从一个端点发送多个时间响应?

如何在EL处理器中定义带有命名空间的变量?

寻找Thread.sky()方法的清晰度

在不使用instanceof或强制转换的情况下从父类变量调用子类方法

如何使用Rascal Evaluator从编译的JAR访问Rascal函数?

使用原子整数的共享计数器并发增量