当前位置: 首页 > 知识库问答 >
问题:

Spring Boot JWT角色和未经授权获取401

方波娃
2023-03-14

我有一个带有/api/users/register和/api/users/loginendpoint的spring boot项目,registerendpoint工作正常,允许我发布以创建新用户(带有验证等),但当我尝试使用正确的详细信息登录时,登录endpoint会给我401(未经授权)响应。

此外,我正在尝试在创建用户时为他们添加新角色,但由于我使用的是JWT,我不确定如何进行此操作,每个在线教程都有不同的实现,我也在努力理解这一点。

如果有人能为我提供一些步骤,我将不胜感激。我知道您需要在生成令牌本身时将角色添加为声明,但我需要在何处实现在创建帐户时分配新角色的实际逻辑。

使用者Java语言

@Entity
@Table(name="user")
public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    @NotBlank(message = "Username is required")
    private String username;

    @NotBlank(message = "Password is required")
    private String password;
    @Transient
    private String confirmPassword;

    private boolean enabled;

    @JsonFormat(pattern = "yyyy-mm-dd")
    private Date createdAt;

    // I want to load all the roles of users + the user itself once requested for
    @ManyToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name="user_roles",
        joinColumns = {
            @JoinColumn(name="user_id")
        },
        inverseJoinColumns = {
            @JoinColumn(name="role_id")
    })
    private Set<Role> roles = new HashSet<>();


    // no-arg and all arg constructor
    // getters & setters



角色Java语言


@Entity
@Table(name="role")
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String description;
    
   
    // constructors
    // getters & setters

**我确实为这两个实体提供了存储库/dao类,并提供了按名称查找用户/角色的方法**

用户服务。Java语言


@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    private RoleService roleService;


    public User saveUser(User user) {

        try {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            user.setUsername(user.getUsername());
            user.setConfirmPassword("");
            user.setEnabled(true);
            
            return userRepository.save(user);
        } catch(Exception e) {
            throw new UsernameAlreadyExistsException("User with username " + user.getUsername() + " already exists!");
        }
    }

}

角色服务。Java语言


@Service
public class RoleService {

    @Autowired
    private RoleRepository roleRepository;

    public Role findByRoleName(String roleName) {
        Role theRole = roleRepository.findByName(roleName);
        return theRole;
    }
}

UserDetailsServiceImpl。Java语言


@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if(user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return (UserDetails) user;
    }
}

这些是我的JWT课程

**JwtAuthenticationFilter。Java语言


public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Autowired
    private JwtTokenProvider tokenProvider;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        try {
            String jwt = getJWTFromRequest(httpServletRequest);

            if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {

                String username = tokenProvider.getUsernameFromJwt(jwt);
                User userDetails = (User) userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, Collections.emptyList());

                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);

    }

    private String getJWTFromRequest(HttpServletRequest request) {
        // Header Authorization: Bearer token
        String bearerToken = request.getHeader("Authorization");

        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7, bearerToken.length());
        }
        return null;
    }
}

JwtTokenProvider.java


@Component
public class JwtTokenProvider {
    // Generate the token
    public String generateToken(Authentication authentication) {
        User user = (User) authentication.getPrincipal();
        Date now = new Date(System.currentTimeMillis());

        Date expiryDate = new Date(now.getTime() + 300_000);

        String userId = Long.toString(user.getId());

        // this is what holds the token
        // add roles in claims
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", (Long.toString(user.getId())));
        claims.put("username", user.getUsername());
        

        return Jwts.builder().setSubject(userId).setClaims(claims).setIssuedAt(now).setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, "SECRETSECRETSECRET").compact();
    }

    // Validate the token
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey("SECRETSECRETSECRET").parseClaimsJws(token);
            return true;
        } catch (SignatureException ex) {
            System.out.println("Invalid JWT Signature");
        } catch (MalformedJwtException ex) {
            System.out.println("Invalid JWT Token");
        } catch (ExpiredJwtException ex) {
            System.out.println("Expired JWT Token");
        } catch (UnsupportedJwtException ex) {
            System.out.println("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            System.out.println("JWT claims string is empty");
        }
        return false;
    }

    public String getUsernameFromJwt(String token) {
        Claims claims = Jwts.parser().setSigningKey("SECRETSECRETSECRET").parseClaimsJws(token).getBody();

        return claims.getSubject();
    }
}

JwtAuthenticationEntryPoint。Java语言


@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        InvalidLoginResponse loginResponse = new InvalidLoginResponse();

        
        // InvalidLoginResponse is a class with username and password fields and a no-arg 
        constructor initialiting them like this

        //  this.username = "Invalid Username";
        //  this.password = "Invalid Password";

        String jsonLoginResponse = new Gson().toJson(loginResponse);

        httpServletResponse.setContentType("application/json");
        httpServletResponse.setStatus(401);
        httpServletResponse.getWriter().print("ERROR FROM JwtAuthenticationEntryPoint: " + jsonLoginResponse);
    }
}

安全Config.java


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

    @Autowired
    private JwtAuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    // Here i am permitting access to all URLs but getting 401 when posting to /login
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests()
                .anyRequest().permitAll();

        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

}

用户控制器。Java语言


@RestController
@RequestMapping("/api/users")
@CrossOrigin
public class UserController {

    private UserService userService;
    private UserDetailsServiceImpl userDetailsService;
    private UserValidator userValidator;
    private ErrorValidationService errorValidationService;
    private JwtTokenProvider tokenProvider;
    private AuthenticationManager authenticationManager;

    @Autowired
    public UserController(UserService userService, UserDetailsServiceImpl userDetailsService, UserValidator userValidator, ErrorValidationService errorValidationService, JwtTokenProvider tokenProvider, AuthenticationManager authenticationManager) {
        this.userService = userService;
        this.userDetailsService = userDetailsService;
        this.userValidator = userValidator;
        this.errorValidationService = errorValidationService;
        this.tokenProvider = tokenProvider;
        this.authenticationManager = authenticationManager;
    }

    // I want to allow role based access to these URLs
    @GetMapping("/all")
    public String welcomeAll() {
        return "Anyone can view this!";
    }

    @GetMapping("/admin")
    public String adminPing(){
        return "Only Admins Can view This";
    }

    @GetMapping("/user")
    public String userPing(){
        return "Any User Can view This";
    }


    @PostMapping("/register")
    public ResponseEntity<?> register(@Valid @RequestBody User user, BindingResult result) {

        userValidator.validate(user, result);
        ResponseEntity<?> errorMap = errorValidationService.validationService(result);
        if(errorMap != null) return errorMap;

        User newUser = userService.saveUser(user);

        return new ResponseEntity<User>(newUser, HttpStatus.CREATED);
    }



    @PostMapping("/login")
    public ResponseEntity<?> generateToken(@Valid @RequestBody LoginRequest loginRequest, BindingResult result) throws Exception {

        System.out.println("Entering /login");
        ResponseEntity<?> errorMap = errorValidationService.validationService(result);
        if(errorMap != null) return errorMap;

        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = "Bearer " + tokenProvider.generateToken(authentication);

        return ResponseEntity.ok(new JwtLoginSuccessResponse(true, jwt));

    }
}

尝试在postman/api/users/register中注册时

当试图登录邮递员 /api/users/register

共有1个答案

陶文林
2023-03-14

当您为所有传入请求添加OncePerRequest estFilter时。因此,当您调用 /api/users/login它将检查JWT令牌,并且无法在标头中找到它,因此它抛出401(未经授权)。要从OncePerRequest estFilter中排除 /api/users/login终端,您需要覆盖应不过滤(HttpServletRequest)

public class LoginFilter extends OncePerRequestFilter {

        private List<String> excludeUrlPatterns = new ArrayList<String>();

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                FilterChain filterChain) throws ServletException, IOException
        {
            //Business logic
        }

       @Override
       protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException 
       {
           // add excludeUrlPatterns on which one to exclude here
       }

    }

有关更多信息,请访问:OncePerRequestFilter

 类似资料:
  • Using a delegation key The collaborator can now push to the repository using Docker Content Trust. Docker will automatically choose and pick the right key for the targets/release role. Edit the file o

  • Importing a delegation certificate As a repository owner: Add the delegation key to the repository using the targets/releases path, as this is what Docker searches for when signing an image (first tar

  • 我的数据未发布在postman api中,这会导致以下错误: 单调的节奏。我的主人。com/api/领域:1个职位http://dradiobeats.x10host.com/api/areas401(未授权)核心。js:4002错误HttpErrorResponse 我的app.component.ts 如何解决此错误?

  • 问题内容: 我从Nexus存储库中检出了代码。我更改了帐户密码,并在文件中正确设置了密码。在执行时,我收到错误消息,说明它尝试从该存储库下载文件。 任何想法如何解决此错误?我在Maven 3.04中使用Windows 7 问题答案: 这里的问题是所使用的密码出现错字错误,由于密码中使用了字符/字母,因此很难识别。

  • Generating a delegation key Make sure you’re using OpenSSL 1.0.2+, otherwise the keys will be signed with SHA1, which Notary will refuse to import (invalid SHA1 signature algorithm): ❯ brew install op

  • 以下是收到的错误消息: “ErrorMessage”:“401未经授权:[{\r\n\”错误“:{\r\n \”代码\“:\”invalidauthenticationToken\“,\r\n\”消息\“:\”访问令牌验证失败。\“,\r\n\”innererror\“:{\r\n \”日期\“:”2020-06-12T11:49:22\“,\r\n\”请求-id\“:”7988BD2E-3274