春季安全和JWT在春季启动应用程序中

时间:2017-08-26 17:11:28

标签: spring-boot spring-security jwt

我在理解春季安全的概念方面苦苦挣扎。我在网上提到了几个例子,并尝试在我的项目中实现它。但它不起作用,我不明白为什么。

我的WebConfig课程是,

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;

@Autowired
private UserDetailsService userDetailsService;

@Autowired
public void configureAuthentication( AuthenticationManagerBuilder authenticationManagerBuilder ) throws Exception
{
    authenticationManagerBuilder.userDetailsService(this.userDetailsService);
}

@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception
{
    return new JwtAuthenticationTokenFilter();
}

@Override
protected void configure( HttpSecurity httpSecurity ) throws Exception
{
    httpSecurity
            // we don't need CSRF because our token is invulnerable
            .csrf().disable()

            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()

            // don't create session
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

            .authorizeRequests()
            //.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()

            // allow anonymous resource requests
            .antMatchers(HttpMethod.GET, "/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js")
            .permitAll().antMatchers("/login").permitAll().anyRequest().authenticated();

    // Custom JWT based security filter
    httpSecurity.addFilterBefore(authenticationTokenFilterBean(), 
UsernamePasswordAuthenticationFilter.class);

    // disable page caching
    httpSecurity.headers().cacheControl();
}
}

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

@Autowired
private UserDetailsService userDetailsService;

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Override
protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain chain )
        throws ServletException, IOException
{
    final String requestHeader = "Authorization";

    String username = null;
    String authToken = null;

    if( requestHeader != null && requestHeader.startsWith("Bearer ") )
    {
        authToken = requestHeader.substring(7);
        try
        {
            username = jwtTokenUtil.getUsernameFromToken(authToken);
        }
        catch( IllegalArgumentException e )
        {
            logger.error("an error occured during getting username from token", e);
        }
    }
    else
    {
        logger.warn("couldn't find bearer string, will ignore the header");
    }

    logger.info("checking authentication for user " + username);
    if( username != null && SecurityContextHolder.getContext().getAuthentication() == null )
    {

        // It is not compelling necessary to load the use details from the database. You could also store the information
        // in the token and read it from it. It's up to you ;)
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
        if( jwtTokenUtil.validateToken(authToken, userDetails) )
        {
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                    userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            logger.info("authenticated user " + username + ", setting security context");
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    }

    chain.doFilter(request, response);
}

}

实施Spring-securities UserDetailsService

@Component
public class JwtUserDetailsServiceImpl implements UserDetailsService {

@Autowired
private UserModelRepository userModelRepository;

@Override
public UserDetails loadUserByUsername( String username ) throws 
UsernameNotFoundException
{
    Optional<UserModel> userModelOptional = 
userModelRepository.findByMobileNumberAndIsActiveTrue(username);

    if( !userModelOptional.isPresent() )
    {
        throw new UsernameNotFoundException(String.format("No user 
found with username '%s'.", username));
    }
    else
    {
        return JwtUserFactory.create(userModelOptional.get());
    }
}
}

UserDetails

的实施
public class JwtUser implements UserDetails {

private final Long id;
private final String username;
private final String firstname;
private final String lastname;
private final String password;
private final String email;
private final boolean enabled;
private final Date lastPasswordResetDate;

public JwtUser( Long id, String username, String firstname, String lastname, String email, String password,
        boolean enabled, Date lastPasswordResetDate )
{
    this.id = id;
    this.username = username;
    this.firstname = firstname;
    this.lastname = lastname;
    this.email = email;
    this.password = password;
    this.enabled = enabled;
    this.lastPasswordResetDate = lastPasswordResetDate;
}

@JsonIgnore
public Long getId()
{
    return id;
}

@Override
public String getUsername()
{
    return username;
}

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

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

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

public String getFirstname()
{
    return firstname;
}

public String getLastname()
{
    return lastname;
}

public String getEmail()
{
    return email;
}

@JsonIgnore
@Override
public String getPassword()
{
    return password;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
    return Collections.emptyList();
}

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

@JsonIgnore
public Date getLastPasswordResetDate()
{
    return lastPasswordResetDate;
}
}

@Component
public class JwtTokenUtil {

static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "aud";
static final String CLAIM_KEY_CREATED = "iat";
static final String AUDIENCE_UNKNOWN = "unknown";
static final String AUDIENCE_WEB = "web";
static final String AUDIENCE_MOBILE = "mobile";
static final String AUDIENCE_TABLET = "tablet";
private static final long serialVersionUID = -3301605591108950415L;
@Autowired
private TimeProvider timeProvider;

private String secret = "MySecret";

private Long expiration = 1508610600000l;

public String getUsernameFromToken( String token )
{
    return getClaimFromToken(token, Claims::getSubject);
}

public Date getIssuedAtDateFromToken( String token )
{
    return getClaimFromToken(token, Claims::getIssuedAt);
}

public Date getExpirationDateFromToken( String token )
{
    return getClaimFromToken(token, Claims::getExpiration);
}

public String getAudienceFromToken( String token )
{
    return getClaimFromToken(token, Claims::getAudience);
}

public <T> T getClaimFromToken( String token, Function<Claims, T> claimsResolver )
{
    final Claims claims = getAllClaimsFromToken(token);
    return claimsResolver.apply(claims);
}

private Claims getAllClaimsFromToken( String token )
{
    return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}

private Boolean isTokenExpired( String token )
{
    final Date expiration = getExpirationDateFromToken(token);
    return expiration.before(timeProvider.now());
}

private Boolean isCreatedBeforeLastPasswordReset( Date created, Date lastPasswordReset )
{
    return (lastPasswordReset != null && created.before(lastPasswordReset));
}

private String generateAudience( Device device )
{
    String audience = AUDIENCE_UNKNOWN;
    if( device.isNormal() )
    {
        audience = AUDIENCE_WEB;
    }
    else if( device.isTablet() )
    {
        audience = AUDIENCE_TABLET;
    }
    else if( device.isMobile() )
    {
        audience = AUDIENCE_MOBILE;
    }
    return audience;
}

private Boolean ignoreTokenExpiration( String token )
{
    String audience = getAudienceFromToken(token);
    return (AUDIENCE_TABLET.equals(audience) || AUDIENCE_MOBILE.equals(audience));
}

public String generateToken( UserDetails userDetails, Device device )
{
    Map<String, Object> claims = new HashMap<>();
    return doGenerateToken(claims, userDetails.getUsername(), generateAudience(device));
}

private String doGenerateToken( Map<String, Object> claims, String subject, String audience )
{
    final Date createdDate = timeProvider.now();
    final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000);

    System.out.println("doGenerateToken " + createdDate);

    return Jwts.builder().setClaims(claims).setSubject(subject).setAudience(audience).setIssuedAt(createdDate)
            .setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
}

public Boolean canTokenBeRefreshed( String token, Date lastPasswordReset )
{
    final Date created = getIssuedAtDateFromToken(token);
    return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
            && (!isTokenExpired(token) || ignoreTokenExpiration(token));
}

public String refreshToken( String token )
{
    final Claims claims = getAllClaimsFromToken(token);
    claims.setIssuedAt(timeProvider.now());
    return doRefreshToken(claims);
}

public String doRefreshToken( Claims claims )
{
    return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
}

public Boolean validateToken( String token, UserDetails userDetails )
{
    JwtUser user = (JwtUser) userDetails;
    final String username = getUsernameFromToken(token);
    final Date created = getIssuedAtDateFromToken(token);
    //final Date expiration = getExpirationDateFromToken(token);
    return (username.equals(user.getUsername()) && !isTokenExpired(token)
            && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()));
}
}

其他控制器,

@RestController
public class AuthenticationRestController {

private String tokenHeader = "Authorization";

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private JwtTokenUtil jwtTokenUtil;

@Autowired
private UserDetailsService userDetailsService;

@RequestMapping( value = "login", method = RequestMethod.POST )
public ResponseEntity<?> createAuthenticationToken( @RequestBody JwtAuthenticationRequest authenticationRequest,
        Device device ) throws AuthenticationException
{

    // Perform the security
    final Authentication authentication = authenticationManager
            .authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(),
                    authenticationRequest.getPassword()));
    SecurityContextHolder.getContext().setAuthentication(authentication);

    // Reload password post-security so we can generate token
    final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
    final String token = jwtTokenUtil.generateToken(userDetails, device);

    // Return the token
    return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
}

在邮递员中,我正在点击http://localhost:8080/login,我正在通过usernamepassword。在我的情况下,usernamepassword都是用户手机号码,我也将其存储在数据库中与手机号码相同。但它显示Bad credentials。我不明白出了什么问题。请帮帮我。

0 个答案:

没有答案