In this article, I will explain how to write simple authentication without the help of ready-made solutions for this task. It can be useful for beginners who want to write their own AAA (Authentication, Authorization, and Accounting). Client repository on Angular and Server repository on Spring .
In this article I will make excerpts of the server-side code in Spring
. └── backendspring ├── BackendspringApplication.java # Spring ├── config │ ├── AppProperties.java │ ├── AuthAuthority.java # │ ├── CorsFilterAdapter.java # CORS │ ├── ErrorMessages.java │ ├── IAuthority.java # │ ├── RequestConstants.java │ ├── DefendedAuthority.java # │ └── SecurityConfig.java # CORS Spring Security ├── controller │ ├── AuthController.java # │ └── ProtectedPingPongController.java # ├── dao │ ├── BaseDao.java │ └── SecureUserDao.java # DAO ├── exception │ ├── AuthException.java │ └── PingPongException.java ├── function │ ├── BaseHandlerFunc.java # ModelHandlerFunc.java │ ├── TrustedHandlerFunc.java # │ └── SecureHandlerFunc.java # ├── model │ ├── Answer.java # - │ ├── AuthUser.java # │ ├── BaseDomain.java │ ├── EnumAuthority.java # Enum │ ├── MessagePayload.java │ ├── MessageResponse.java │ ├── Payload.java # / JSON │ ├── PingPayload.java # , │ ├── PongPayload.java # , │ ├── UserCredentials.java # / │ └── SecureUser.java # └── service ├── PingPongService.java # , ├── SecureUserService.java # ***, /*** └── SecureUtils.java #
SecureUserService
main service of this article - for the sake of what she thought.
It implements the following methods:
public Optional<AuthUser> register(UserCredentials usercredentials)
- User registration;
public Optional<AuthUser> authorize(UserCredentials usercredentials)
- Authorization or user login;
public Optional<AuthUser> authenticate(AuthUser authUser)
- Authentication or User Rights Verification;
public Optional<AuthUser> logout(AuthUser authUser)
- public Optional<AuthUser> logout(AuthUser authUser)
or Delete information that a user is currently online.
I will give the user authorization code:
// String credentials = usercredentials.getCredentials(); String salt = secureUser.getSalt(); String clientDigest = SecureUtils.digest(credentials + salt); // if (clientDigest.equals(secureUser.getDigest())) { // AccessToken SecureUser TokenPair accessToken = getAccessToken(secureUser); // String userSession = getUserSession(); // AccessToken secureUser.setSecureToken(accessToken.secureToken); secureUser.setAccessToken(accessToken.accessToken); secureUser.setUserSession(userSession); secureUserDao.save(secureUser); // AccessToken, String userId = secureUser.getId(); Set<EnumAuthority> authorities = secureUser.getAuthorities(); AuthUser authUser = AuthUser.simpleUser(userId, username, accessToken.accessToken, userSession, authorities); return authUser; }
In general, the standard algorithm.
Yes, I do not use any user data to get AccessToken. I just generate a random string and encrypt it with standard javax.crypto encryption algorithms.
To form a response to the client, I used the method described earlier in this article.
In this example, I made some simplifications. But, here, functional interfaces from Java SE 8 are still used:
I will give an example of how I respond to a client's request after authorizing it on the site:
@PostMapping("authorize") public @ResponseBody Answer authorize(@RequestBody UserCredentials usercredentials, HttpServletResponse response) { // return ((TrustedHandlerFunc<UserCredentials>) (data) -> secureUserService.authorize(data) .map(Answer::ok) .orElseGet(Answer::forbidden)) .handleAuthRequest(response, usercredentials); }
To handle unauthorized requests, I use the TrustedHandlerFunc
functional interface. It contains the Answer process(T data)
method. This method is implemented in the controller and it calls the SecureUserService::authorize
method. The response of this service is glued to the Answer::ok
method in case of successful authorization or the Answer::forbidden
method in case of unsuccessful authorization. Also, the interface has a default method TrustedHandlerFunc::handleRequest
and TrustedHandlerFunc::handleAuthRequest
, which select Answer process(T data)
for the Answer process(T data)
method. Here is the UserCredentials
. It is necessary to clarify that the first method handleRequest
assumes the presence of the AuthUser
token, and the second, handleAuthRequest
, is needed only for the AuthController
controller.
Consider a custom query handler. Call it PingPongService
. By convention, this controller should not be accessible to unauthorized clients.
I will give an example of creating a response to a ping
request:
@PostMapping("ping") public @ResponseBody Answer ping(@RequestBody PingPayload ping, HttpServletRequest request, HttpServletResponse response) { return authenticateRequestService .getAuthenticatedUser(request, DefendedAuthority.PING) .map(authUser -> // ((TrustedHandlerFunc<PingPayload>) (data) -> pingPongService.getPong(data, authUser) // .map(Answer::ok) .orElseGet(Answer::forbidden) ).handleRequest(response, ping, authUser) // ).orElseThrow(AuthException::forbidden); }
It uses two functional interfaces: SecureHandlerFunc
and TrustedHandlerFunc
. The first one checks the custom headers that come from the client, creates an AuthUser
"token" from them, and passes them to the next method of the TrustedHandlerFunc
interface. Here, the token is expected to be an authorized user.
I will not give the details of the implementation of these interfaces, since they are already described in the article mentioned earlier. Let me just say that the only difference is in the division of duties on the authorization of the data received in the headers and sending the result to the client.
It should be noted that still had to connect Spring Security to work with CORS.
To add the necessary headers, the code with StackOverflow was used and slightly reworked. It is in the CorsFilterAdapter
and SecurityConfig
classes.
In this article, we looked at how to do simple do-it-yourself authentication.
Source: https://habr.com/ru/post/354862/
All Articles