diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/auth/controller/AuthenticationController.java b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/controller/AuthenticationController.java new file mode 100644 index 00000000..c66deda4 --- /dev/null +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/controller/AuthenticationController.java @@ -0,0 +1,45 @@ +package com.jmsoftware.apiportal.auth.controller; + +import com.jmsoftware.apiportal.auth.entity.LoginPayload; +import com.jmsoftware.apiportal.auth.entity.LoginResponse; +import com.jmsoftware.apiportal.auth.entity.RegisterPayload; +import com.jmsoftware.apiportal.auth.service.AuthenticationService; +import com.jmsoftware.common.bean.ResponseBodyBean; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + *

AuthenticationController

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/8/20 11:06 AM + **/ +@RestController +@RequiredArgsConstructor +@RequestMapping("/authentication") +@Api(tags = {"Authentication Controller"}) +public class AuthenticationController { + private final AuthenticationService authenticationService; + + @PostMapping("/register") + @ApiOperation(value = "/register", notes = "Register (create an account)") + public ResponseBodyBean register(@Valid @RequestBody RegisterPayload payload) { + authenticationService.register(payload); + return ResponseBodyBean.ofSuccess(); + } + + @PostMapping("/login") + @ApiOperation(value = "/login", notes = "Login") + public ResponseBodyBean login(@Valid @RequestBody LoginPayload payload) { + return ResponseBodyBean.ofSuccess(authenticationService.login(payload)); + } +} diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/LoginPayload.java b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/LoginPayload.java new file mode 100644 index 00000000..44ab8fea --- /dev/null +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/LoginPayload.java @@ -0,0 +1,36 @@ +package com.jmsoftware.apiportal.auth.entity; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + *

LoginPayload

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/8/20 2:08 PM + **/ +@Data +public class LoginPayload { + /** + * The Login token: username / email + */ + @NotEmpty + @Length(max = 100) + private String loginToken; + /** + * The Password. + */ + @NotEmpty + @Length(max = 60) + private String password; + /** + * Remember me + */ + @NotNull + private Boolean rememberMe; +} diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/LoginResponse.java b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/LoginResponse.java new file mode 100644 index 00000000..e8f0c87a --- /dev/null +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/LoginResponse.java @@ -0,0 +1,16 @@ +package com.jmsoftware.apiportal.auth.entity; + +import lombok.Data; + +/** + *

LoginResponse

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/8/20 2:07 PM + **/ +@Data +public class LoginResponse { + private String jwt; +} diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/RegisterPayload.java b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/RegisterPayload.java new file mode 100644 index 00000000..84055d5f --- /dev/null +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/entity/RegisterPayload.java @@ -0,0 +1,37 @@ +package com.jmsoftware.apiportal.auth.entity; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; + +/** + *

RegisterPayload

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/9/20 3:53 PM + **/ +@Data +public class RegisterPayload { + /** + * Username (Unique) + */ + @NotEmpty + @Length(min = 4, max = 50) + private String username; + /** + * Email (Unique) + */ + @NotEmpty + @Size(max = 30) + private String email; + /** + * Password + */ + @NotEmpty + @Length(min = 8, max = 30) + private String password; +} diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/auth/service/AuthenticationService.java b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/service/AuthenticationService.java new file mode 100644 index 00000000..e92f12ac --- /dev/null +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/service/AuthenticationService.java @@ -0,0 +1,30 @@ +package com.jmsoftware.apiportal.auth.service; + +import com.jmsoftware.apiportal.auth.entity.LoginPayload; +import com.jmsoftware.apiportal.auth.entity.LoginResponse; +import com.jmsoftware.apiportal.auth.entity.RegisterPayload; + +/** + *

AuthenticationService

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/8/20 2:04 PM + */ +public interface AuthenticationService { + /** + * Register. + * + * @param payload the payload + */ + void register(RegisterPayload payload); + + /** + * Login login response. + * + * @param payload the payload + * @return the login response + */ + LoginResponse login(LoginPayload payload); +} diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/auth/service/impl/AuthenticationServiceImpl.java b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/service/impl/AuthenticationServiceImpl.java new file mode 100644 index 00000000..c67e077d --- /dev/null +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/auth/service/impl/AuthenticationServiceImpl.java @@ -0,0 +1,55 @@ +package com.jmsoftware.apiportal.auth.service.impl; + +import com.jmsoftware.apiportal.auth.entity.LoginPayload; +import com.jmsoftware.apiportal.auth.entity.LoginResponse; +import com.jmsoftware.apiportal.auth.entity.RegisterPayload; +import com.jmsoftware.apiportal.auth.service.AuthenticationService; +import com.jmsoftware.apiportal.universal.domain.UserPO; +import com.jmsoftware.apiportal.universal.service.JwtService; +import com.jmsoftware.apiportal.universal.service.UserService; +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +/** + *

AuthenticationServiceImpl

+ *

+ * Change description here. + * + * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com + * @date 5/8/20 2:04 PM + **/ +@Service +@RequiredArgsConstructor +public class AuthenticationServiceImpl implements AuthenticationService { + private final BCryptPasswordEncoder encoder; + private final AuthenticationManager authenticationManager; + private final JwtService jwtService; + private final UserService userService; + + @Override + public void register(RegisterPayload payload) { + val userPo = new UserPO(); + userPo.setUsername(payload.getUsername()); + userPo.setEmail(payload.getEmail()); + userPo.setPassword(encoder.encode(payload.getPassword())); + // TODO: user service should be in the `auth-center` + userService.saveUser(userPo); + } + + @Override + public LoginResponse login(LoginPayload payload) { + val authenticationToken = new UsernamePasswordAuthenticationToken(payload.getLoginToken(), + payload.getPassword()); + val authentication = authenticationManager.authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + val jwt = jwtService.createJwt(authentication, payload.getRememberMe()); + val loginResponse = new LoginResponse(); + loginResponse.setJwt(jwt); + return loginResponse; + } +} diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/universal/aspect/ExceptionControllerAdvice.java b/api-portal/src/main/java/com/jmsoftware/apiportal/universal/aspect/ExceptionControllerAdvice.java index 2c8bcab9..03230056 100644 --- a/api-portal/src/main/java/com/jmsoftware/apiportal/universal/aspect/ExceptionControllerAdvice.java +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/universal/aspect/ExceptionControllerAdvice.java @@ -11,6 +11,7 @@ import org.springframework.context.support.DefaultMessageSourceResolvable; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -117,6 +118,16 @@ public ResponseBodyBean handleException(HttpServletRequest request, log.error("[GlobalExceptionCapture]: Exception information: {} ", exception.getMessage()); response.setStatus(HttpStatus.BAD_CREDENTIALS.getCode()); return ResponseBodyBean.ofStatus(HttpStatus.BAD_CREDENTIALS.getCode(), exception.getMessage(), null); + } else if (exception instanceof InternalAuthenticationServiceException) { + log.error("[GlobalExceptionCapture]: Exception information: {} ", exception.getMessage()); + if (exception.getCause() instanceof BaseException) { + val exceptionCause = (BaseException) exception.getCause(); + val code = exceptionCause.getCode(); + response.setStatus(code); + return ResponseBodyBean.ofStatus(HttpStatus.fromCode(code)); + } + response.setStatus(HttpStatus.ERROR.getCode()); + return ResponseBodyBean.ofStatus(HttpStatus.BAD_CREDENTIALS.getCode(), exception.getMessage(), null); } log.error("[GlobalExceptionCapture]: Exception information: {} ", exception.getMessage(), exception); response.setStatus(HttpStatus.ERROR.getCode()); diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/UserService.java b/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/UserService.java index dd1457f9..c46eb974 100644 --- a/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/UserService.java +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/UserService.java @@ -15,8 +15,8 @@ *

Change description here

* * @author Johnny Miller (鍾俊), email: johnnysviva@outlook.com - * @date 2019-06-07 11:42 - **/ + * @date 2019 -06-07 11:42 + */ public interface UserService { /** * Get user page list @@ -54,7 +54,7 @@ public interface UserService { * Search user by username * * @param username username - * @return user + * @return user user po */ UserPO searchUserByUsername(String username); @@ -103,8 +103,15 @@ public interface UserService { * Get user's avatar resource * * @param username username - * @return user's avatar + * @return user 's avatar * @throws IOException IO exception */ ByteArrayResource getUserAvatarResource(String username) throws IOException; + + /** + * Save user. + * + * @param userPo the user po + */ + void saveUser(UserPO userPo); } diff --git a/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/impl/UserServiceImpl.java b/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/impl/UserServiceImpl.java index 7f468b5b..259ffdf1 100644 --- a/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/impl/UserServiceImpl.java +++ b/api-portal/src/main/java/com/jmsoftware/apiportal/universal/service/impl/UserServiceImpl.java @@ -128,4 +128,9 @@ public ByteArrayResource getUserAvatarResource(String username) throws IOExcepti @Cleanup InputStream stream = sftpService.read(po.getAvatar()); return new ByteArrayResource(stream.readAllBytes()); } + + @Override + public void saveUser(UserPO userPo) { + userMapper.register(userPo); + } } diff --git a/api-portal/src/main/resources/mapper/rbac/UserMapper.xml b/api-portal/src/main/resources/mapper/rbac/UserMapper.xml index 8ed88731..885372b4 100644 --- a/api-portal/src/main/resources/mapper/rbac/UserMapper.xml +++ b/api-portal/src/main/resources/mapper/rbac/UserMapper.xml @@ -67,17 +67,17 @@ INSERT INTO user(username, - email, - cellphone, - password, - full_name, - birthday, - gender, - created_time, - modified_time, - status) + email, + cellphone, + password, + full_name, + birthday, + gender, + created_time, + modified_time, + status) VALUES (#{username}, - #{password}, + #{email}, #{cellphone}, #{password}, #{fullName}, @@ -89,8 +89,18 @@ - INSERT INTO user(username, password, email, created_time, modified_time, status) - VALUES (#{username}, #{password}, #{email}, now(), now(), 1) + INSERT INTO user(username, + password, + email, + created_time, + modified_time, + status) + VALUES (#{username}, + #{password}, + #{email}, + now(), + now(), + 1)