我在理解春季安全的概念方面苦苦挣扎。我在网上提到了几个例子,并尝试在我的项目中实现它。但它不起作用,我不明白为什么。
我的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
,我正在通过username
和password
。在我的情况下,username
和password
都是用户手机号码,我也将其存储在数据库中与手机号码相同。但它显示Bad credentials
。我不明白出了什么问题。请帮帮我。