Task: Completed login and get token from user and able to change user password by username and updated the security filters and add auth service and auth details. And more add login mutation in resolver

This commit is contained in:
Sambo Chea 2021-08-08 18:55:52 +07:00
parent 9202c52640
commit ceaacc9685
12 changed files with 167 additions and 37 deletions

@ -1 +1 @@
Subproject commit 9a114dba23c52619955999f867bc1b95d3870bb3 Subproject commit d69f52fee0d6c8c0fad3eab64dc0645470168a49

View File

@ -9,7 +9,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@EnableWebMvc @EnableWebMvc
class WebConfig : WebMvcConfigurer { class WebConfig : WebMvcConfigurer {
override fun addCorsMappings(corsRegistry: CorsRegistry) { override fun addCorsMappings(corsRegistry: CorsRegistry) {
println("Hello World")
corsRegistry.addMapping("/**") corsRegistry.addMapping("/**")
.allowedOrigins("*") .allowedOrigins("*")
.allowedMethods("*") .allowedMethods("*")

View File

@ -1,6 +1,6 @@
package com.cubetiqs.graphql.demo.config package com.cubetiqs.graphql.demo.config
import com.cubetiqs.graphql.demo.secutiry.AuthService import com.cubetiqs.graphql.demo.security.AuthService
import com.cubetiqs.security.jwt.AuthenticationExceptionEntryPoint import com.cubetiqs.security.jwt.AuthenticationExceptionEntryPoint
import com.cubetiqs.security.jwt.JwtSecurityConfigurer import com.cubetiqs.security.jwt.JwtSecurityConfigurer
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
@ -13,26 +13,25 @@ import org.springframework.security.config.http.SessionCreationPolicy
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig : WebSecurityConfigurerAdapter() { class WebSecurityConfig : WebSecurityConfigurerAdapter() {
@Autowired @Autowired
private lateinit var authService: AuthService private lateinit var authService: AuthService
override fun configure(http: HttpSecurity) { override fun configure(http: HttpSecurity) {
http.csrf() http.csrf().disable()
.and()
.httpBasic()
.disable()
.sessionManagement() .sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
http
.exceptionHandling() .exceptionHandling()
.authenticationEntryPoint(AuthenticationExceptionEntryPoint()) .authenticationEntryPoint(AuthenticationExceptionEntryPoint())
.and()
http
.apply(JwtSecurityConfigurer(authService)) .apply(JwtSecurityConfigurer(authService))
.and()
http
.authorizeRequests() .authorizeRequests()
.anyRequest() .anyRequest().permitAll()
.permitAll()
} }
} }

View File

@ -6,6 +6,7 @@ import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.util.*
@Repository @Repository
interface UserRepository : JpaRepository<User, Long> { interface UserRepository : JpaRepository<User, Long> {
@ -14,4 +15,7 @@ interface UserRepository : JpaRepository<User, Long> {
@Query("select (count(u) > 0) from User u where u.username = ?1") @Query("select (count(u) > 0) from User u where u.username = ?1")
fun existsAllByUsername(username: String): Boolean fun existsAllByUsername(username: String): Boolean
@Query("select u from User u where u.username = ?1")
fun queryByUsername(username: String): Optional<User>
} }

View File

@ -0,0 +1,22 @@
package com.cubetiqs.graphql.demo.resolver.mutation
import com.cubetiqs.graphql.demo.context.GMutation
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
import com.cubetiqs.graphql.demo.dgmodel.types.LoginResponse
import com.cubetiqs.graphql.demo.security.AuthService
import com.cubetiqs.security.jwt.util.JwtUtils
import com.netflix.graphql.dgs.DgsMutation
import org.springframework.beans.factory.annotation.Autowired
@GMutation
class LoginMutationResolver {
@Autowired
private lateinit var authService: AuthService
@DgsMutation(field = DgsConstants.MUTATION.Login)
fun login(username: String, password: String): LoginResponse {
val auth = authService.login(username, password)
val token = JwtUtils.encryptToken(auth)
return LoginResponse(token)
}
}

View File

@ -2,11 +2,14 @@ package com.cubetiqs.graphql.demo.resolver.mutation
import com.cubetiqs.graphql.demo.context.GMutation import com.cubetiqs.graphql.demo.context.GMutation
import com.cubetiqs.graphql.demo.dgmodel.DgsConstants import com.cubetiqs.graphql.demo.dgmodel.DgsConstants
import com.cubetiqs.graphql.demo.dgmodel.types.UserChangePasswordInput
import com.cubetiqs.graphql.demo.domain.user.User import com.cubetiqs.graphql.demo.domain.user.User
import com.cubetiqs.graphql.demo.domain.user.UserInput import com.cubetiqs.graphql.demo.domain.user.UserInput
import com.cubetiqs.graphql.demo.domain.user.UserMapper import com.cubetiqs.graphql.demo.domain.user.UserMapper
import com.cubetiqs.graphql.demo.repository.UserRepository import com.cubetiqs.graphql.demo.repository.UserRepository
import com.cubetiqs.security.jwt.util.JwtUtils
import com.netflix.graphql.dgs.DgsMutation import com.netflix.graphql.dgs.DgsMutation
import com.netflix.graphql.dgs.exceptions.DgsEntityNotFoundException
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.transaction.annotation.Propagation import org.springframework.transaction.annotation.Propagation
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
@ -18,9 +21,16 @@ class UserMutationResolver @Autowired constructor(
@DgsMutation(field = DgsConstants.MUTATION.CreateUser) @DgsMutation(field = DgsConstants.MUTATION.CreateUser)
@Transactional(propagation = Propagation.REQUIRES_NEW) @Transactional(propagation = Propagation.REQUIRES_NEW)
fun createUser(input: UserInput): User { fun createUser(input: UserInput): User {
if (userRepository.existsAllByUsername(input.username ?: "")) throw Exception("Username has been already existed!") if (userRepository.existsAllByUsername(input.username ?: "")) throw DgsEntityNotFoundException("Username has been already existed!")
val user = UserMapper.fromInputToUser(input) val user = UserMapper.fromInputToUser(input)
return userRepository.save(user) return userRepository.save(user)
} }
@DgsMutation(field = DgsConstants.MUTATION.ChangeUserPassword)
fun changePassword(input: UserChangePasswordInput): User {
val user = userRepository.queryByUsername(input.username).orElse(null) ?: throw DgsEntityNotFoundException("User not found!")
user.password = JwtUtils.passwordEncoder().encode(input.password)
return userRepository.save(user)
}
} }

View File

@ -0,0 +1,64 @@
package com.cubetiqs.graphql.demo.security
import com.cubetiqs.graphql.demo.domain.user.User
import com.cubetiqs.security.jwt.util.JwtUtils
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
data class AuthDetails(
private var code: String? = null,
private var name: String? = null,
private var username: String? = null,
private var password: String? = null,
private var authorities: Collection<String>? = null,
private var enabled: Boolean? = null,
) : UserDetails {
override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
return authorities?.map { SimpleGrantedAuthority(it) }?.toMutableList() ?: mutableListOf(
SimpleGrantedAuthority(
"USER"
)
)
}
override fun getPassword(): String {
return password ?: ""
}
override fun getUsername(): String {
return username ?: ""
}
override fun isAccountNonExpired(): Boolean {
return true
}
override fun isAccountNonLocked(): Boolean {
return true
}
override fun isCredentialsNonExpired(): Boolean {
return true
}
override fun isEnabled(): Boolean {
return enabled ?: false
}
fun isPasswordValid(password: String): Boolean {
return JwtUtils.passwordEncoder().matches(password, this.getPassword())
}
companion object {
fun fromUser(user: User): AuthDetails {
return AuthDetails(
code = user.code,
name = user.name,
username = user.username,
password = user.password,
enabled = user.enabled,
)
}
}
}

View File

@ -0,0 +1,32 @@
package com.cubetiqs.graphql.demo.security
import com.cubetiqs.graphql.demo.repository.UserRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.stereotype.Service
@Service
class AuthService : UserDetailsService {
@Autowired
private lateinit var userRepository: UserRepository
private fun findAuthDetailsByUsername(username: String): AuthDetails {
val user = userRepository.queryByUsername(username).orElse(null) ?: throw Exception("User not found!")
return AuthDetails.fromUser(user)
}
override fun loadUserByUsername(username: String?): UserDetails {
return findAuthDetailsByUsername(username ?: "")
}
fun login(username: String, password: String): AuthDetails {
val auth = findAuthDetailsByUsername(username)
if (auth.isPasswordValid(password)) {
return auth
} else {
throw AccessDeniedException("Username and password is incorrect!")
}
}
}

View File

@ -1,12 +0,0 @@
package com.cubetiqs.graphql.demo.secutiry
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.stereotype.Service
@Service
class AuthService : UserDetailsService {
override fun loadUserByUsername(username: String?): UserDetails {
TODO("Not yet implemented")
}
}

View File

@ -15,7 +15,10 @@ type Subscription {
} }
type Mutation { type Mutation {
login(username: String!, password: String!): LoginResponse
createUser(input: UserInput): User! createUser(input: UserInput): User!
changeUserPassword(input: UserChangePasswordInput): User!
openAccount(input: AccountInput): Account! openAccount(input: AccountInput): Account!
} }

View File

@ -11,4 +11,13 @@ input UserInput {
password: String password: String
name: String name: String
enabled: Boolean enabled: Boolean
}
type LoginResponse {
token: String
}
input UserChangePasswordInput {
username: String!
password: String!
} }

View File

@ -40,23 +40,23 @@ const HELLO = gql`
` `
function App() { function App() {
// const {error, loading, data} = useQuery<AccountResult>(ACCOUNTS) const {error, loading, data} = useQuery<AccountResult>(ACCOUNTS)
const {error, loading, data} = useSubscription(HELLO) // const {error, loading, data} = useSubscription(HELLO)
console.log(data) console.log(data)
return ( return (
<> <>
<h1>Accounts</h1> <h1>Accounts</h1>
{ {
loading || !data ? <p>Loading...</p> : loading || !data ? <p>Loading...</p> :
// data.fetchAccounts.map(account => ( data.fetchAccounts.map(account => (
// <> <>
// <div>Account ID: {account.id}</div> <div>Account ID: {account.id}</div>
// <div>Account Code: {account.code}</div> <div>Account Code: {account.code}</div>
// <div>Account User: {account.user.name}</div> <div>Account User: {account.user.name}</div>
// </> </>
// ) )
// ) )
<p>{`${data.hello}`}</p> // <p>{`${data.hello}`}</p>
} }
</> </>
); );