[英]Spring Security jwt authentication
我正在使用 spring 引導和 jwt 創建一個后端應用程序以進行身份驗證。
我的問題是我無法讓當局按照我的意願工作。 我可以登錄我的用戶並取回 jwt。 但是,當在服務器上請求僅允許特定權限的路徑時,即使我沒有發送授權 header,我也會返回 200。
這是我的代碼:
安全配置.kt
@EnableWebSecurity
class SecurityConfig(
private val userDetailsService: UserDetailsService,
private val jwtAuthenticationFilter: JwtAuthenticationFilter)
: WebSecurityConfigurerAdapter() {
override fun configure(httpSecurity: HttpSecurity) {
httpSecurity.csrf().disable()
.authorizeRequests()
.antMatchers( "/${Constants.API_PATH}/${Constants.USER_PATH}/**") // translates to /api/v1/users/**
.hasAuthority("USER")
.antMatchers("/${Constants.API_PATH}/${Constants.EMAIL_VERIFICATION_PATH}/**")
.permitAll()
.antMatchers("/${Constants.API_PATH}/${Constants.LOGIN_PATH}")
.permitAll()
.anyRequest()
.authenticated()
httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter::class.java)
}
override fun configure(auth: AuthenticationManagerBuilder) {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder())
}
@Bean
fun encoder(): PasswordEncoder {
return BCryptPasswordEncoder() // salts the password
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
override fun authenticationManagerBean(): AuthenticationManager {
return super.authenticationManagerBean()
}
}
LoginService.kt (不知道這段代碼是否相關,但也許這里有問題)
@Service
class LoginService(private val authenticationManager: AuthenticationManager,
private val jwtProvider: JwtProvider,
private val userService: UserService) {
fun login(loginRequest: LoginRequest): LoginResponse {
// authenticate internally call UserDetailsService
val authenticate = authenticationManager.authenticate(
UsernamePasswordAuthenticationToken(loginRequest.email, loginRequest.password))
SecurityContextHolder.getContext().authentication = authenticate
//TODO implement logic for employer
val jwtToken = jwtProvider.generateToken(authenticate)
val jobseeker = jobseekerService.getJobseeker(loginRequest.email)
return LoginResponse(jwtToken, jobseeker)
}
UserDetailsServiceImpl.kt
@Service
class UserDetailsServiceImpl(private val userRepository: UserRepository) : UserDetailsService {
/* we don't use usernames, so we pass the email address here*/
override fun loadUserByUsername(username: String?): UserDetails {
val user = userRepository.findByEmail(username) ?: throw CustomException("jobseeker not found")
return org.springframework.security.core.userdetails.User(user.email, user.getHashedSecret(),
jobseeker.getActivated(), true, true, true,
getAuthorities("USER"))
}
private fun getAuthorities(authority: String) = singletonList(SimpleGrantedAuthority(authority))
}
JwtProvider.kt (我知道“秘密”不是一個好秘密,它只是為了測試目的而在這里)
@Service
class JwtProvider {
val secret: String = "secret"
// TODO: IMPORTANT -> the keystore is selfsigned and needs to be changed as soon as we get
// to the first production version
private lateinit var keyStore: KeyStore
@PostConstruct
fun init() {
try {
keyStore = KeyStore.getInstance("JKS")
// very helpful for resources: https://stackoverflow.com/questions/4301329/java-class-getresource-returns-null
val resourceAsStream: InputStream? = javaClass.getResourceAsStream("/key.jks")
keyStore.load(resourceAsStream, secret.toCharArray())
} catch (ex: Exception) {
throw CustomException("problem while loading keystore")
}
}
fun generateToken(authentication: Authentication): String {
val pricipal: User = authentication.principal as User
return Jwts.builder()
.setSubject(pricipal.username)
.signWith(getPrivateKey())
.compact()
}
private fun getPrivateKey(): PrivateKey {
return try {
keyStore.getKey("key", secret.toCharArray()) as PrivateKey
} catch (ex: Exception) {
logger.error(ex.message)
throw CustomException("problem while retreiving the private key for jwt signing")
}
}
private fun getPublicKey(): PublicKey {
return try {
keyStore.getCertificate("key").publicKey
} catch (ex: KeyStoreException) {
throw CustomException("problem while retreiving public key")
}
}
fun validateToken(jwt: String): Boolean {
// using parseClaimsJws() because the jwt is signed
Jwts.parserBuilder().setSigningKey(getPublicKey()).build().parseClaimsJws(jwt)
return true
}
fun getEmailFromJwt(jwt: String): String {
return Jwts.parserBuilder().setSigningKey(getPublicKey()).build().parseClaimsJws(jwt).body.subject
}
}
JwtAuthenticationFilter.kt
@Component
class JwtAuthenticationFilter(
private val jwtProvider: JwtProvider,
private val userDetailsService: UserDetailsService
) : OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val jwt: String = getJwtFromRequest(request)
if (StringUtils.hasText(jwt) && jwtProvider.validateToken(jwt)) {
val email: String = jwtProvider.getEmailFromJwt(jwt)
val userDetails: UserDetails = userDetailsService.loadUserByUsername(email)
val authentication = UsernamePasswordAuthenticationToken(userDetails, null, userDetails.authorities)
authentication.details = (WebAuthenticationDetailsSource().buildDetails(request))
SecurityContextHolder.getContext().authentication = authentication
}
filterChain.doFilter(request, response)
}
fun getJwtFromRequest(request: HttpServletRequest): String {
val bearerToken: String = request.getHeader("Authorization") ?: ""
// hasText() is needed because there are api without auth and this string could be null
if(StringUtils.hasText(bearerToken))
return bearerToken.substringAfter(" ")
return bearerToken
}
}
So when I now try GET http://localhost:8080/api/v1/users
or GET http://localhost:8080/api/v1/users/<uuid>
without the jwt as a bearer token I get back a 200而不是 403。
我已經嘗試了好幾個小時了,完全被卡住了。
我對 kotlin 和 spring 啟動也相當陌生,如果我能以更優雅的方式編寫代碼,我將不勝感激。
如果jwt
在JwtAuthenticationFilter
中為空,您可以簡單地拋出異常:
...
val jwt: String = getJwtFromRequest(request)
if (jwt.isEmpty()) {
throw new AccessDeniedException("Missing JWT Token")
}
...
或者,您可以執行以下操作:
...
val jwt: String = getJwtFromRequest(request)
if (jwt.isEmpty()) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.