Thanks a million for your reply!

Lots of information for me to take this a step further.

/Bengt

Den tors 22 nov. 2018 kl 09:22 skrev armandoxxx <[email protected]>:

> Explanation of our project security:
>
> Clippitamine security uses Apache Shiro as underlaying security library.
> Shiro was used, because it allowed both simple and robust security
> requirements.
> Clippitamine-Shiro security filter implementations:
>
>
> cTokenAuthzFilter
> Filter checking (in isAccessAllowed() method) if JWT token exists in
> Authorization: request header and checks if token is valid.
>
> cRestAuthFilter
> Resolves request body for login request details and executes login.
>
> cFormAuthFilter
> Resolves login request details from application/x-www-form-urlencoded
> parameters and executes login.
>
> cHeaderAuthFilter
> Resolved login request details from request headers and executes login.
>
> cTokenAuthFilter
> Resolves login request details from request parameter and executes login
>
> cLoginRequiredFilter
> Allways redirects to loginRequiredUri
>
> Shiro security filter implementation properties:
>
>
> How they work together:
> *AuthFilter implementations only serve for login purposes. Meaning that
> filter chain is executed until one of the filters succedes with login user.
> If none of the filters resolves login request, last filter
> cLoginRequiredFilter is executed and it always redirects to login required
> end point.
> *AuthzFilter implementations are used for general checking if user is
> allowed or denied access. For exmaple, checks validity of JWT token.
>
> REST API security:
> - with JWT
>
> filter chain on endpoint /**
>
> noSessionCreation
> cTokenTAuthzFilter
> cRestAuthFilter
> cFormAuthFilter
> cTokenAuthFilter
> cLoginRequiredFilter
>
> filter chain on endpoint /loginRequired
>
> anon
> noSessionCreation
>
>
> filter chain on endpoint /invalidCredentials
>
> anon
> noSessionCreation
>
>
>
> MEDIA API security:
> without JWT
>
> endpoinds secured by filters:
>
> cTokenAuthFilter
> dcLoginRequiredFilter
>
>
> ----------------------------------------------------------------------------
>
> Here is full code (mind our classes and package names .. change what you
> need .. here is the code that works)
>
> StringToken code
>
> package com.clippitamine.security.core.tokens;
>
> import org.apache.shiro.authc.AuthenticationToken;
>
> public class StringTokenAuthenticationToken implements AuthenticationToken
> {
>
>
>   private String token    = null;
>   private String ip         = null;
>
>
>   public StringTokenAuthenticationToken(final String theTheToken, final
> String theIp) {
>     token    = theTheToken;
>     ip        = theIp;
>   }
>
>   public String getIp() {
>     return ip;
>   }
>
>   public void setIp(String ip) {
>     this.ip = ip;
>   }
>
>   public boolean hasIp() {
>     return this.ip != null && !this.ip.isEmpty();
>   }
>
>   @Override
>   public Object getCredentials() {
>     return this.token;
>   }
>
>   @Override
>   public Object getPrincipal() {
>     return this.token;
>   }
> }
>
> Base auth filter
> package com.clippitamine.security.core.filters;
>
> import lombok.Data;
> import org.apache.shiro.authc.AuthenticationException;
> import org.apache.shiro.authc.AuthenticationToken;
> import org.apache.shiro.subject.Subject;
> import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
> import org.apache.shiro.web.util.WebUtils;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> import org.springframework.beans.factory.annotation.Value;
>
> import javax.servlet.ServletRequest;
> import javax.servlet.ServletResponse;
> import java.io.IOException;
>
>
> @Data
> abstract public class BaseAuthFilter extends AuthenticatingFilter {
>
>
>     final Logger log;
>
>     /**
>      * URL on failed login request
>      **/
>     @Value("#{ @environment['clippitamine.security.loginFailUrl'] ?:
> \"/invalidCredentials\"}")
>     String failUrl;
>
>     /**
>      * URL on not logged in request
>      **/
>     @Value("#{ @environment['clippitamine.security.loginInfoUrl'] ?:
> \"/loginInfo\"}")
>     String loginInfoUrl;
>
>     /**
>      * username param name
>      */
>     @Value("#{ @environment['clippitamine.security.usernameParam'] ?:
> \"username\"}")
>     String usernameParam;
>
>     /**
>      * username param name
>      */
>     @Value("#{ @environment['clippitamine.security.passwordParam'] ?:
> \"password\"}")
>     String passwordParam;
>
>
>     public BaseAuthFilter()
>     {
>         this.log = LoggerFactory.getLogger(RestAuthFilter.class);
>     }
>
>
>     protected boolean executeLogin(AuthenticationToken token,
> ServletRequest
> request, ServletResponse response) throws Exception {
>         if (token == null) {
>             String msg = "createToken method implementation returned null.
> A
> valid non-null AuthenticationToken " +
>                     "must be created in order to execute a login attempt.";
>             throw new IllegalStateException(msg);
>         }
>         this.log.info("Executing login for [{}]", token.getPrincipal());
>         try {
>             Subject subject = getSubject(request, response);
>             subject.login(token);
>             this.log.info("Login success for [{}]", token.getPrincipal());
>             return onLoginSuccess(token, subject, request, response);
>         } catch (AuthenticationException e) {
>             this.log.error("Cannot login user [{}] ", token.getPrincipal(),
> e);
>             return onLoginFailure(token, e, request, response);
>         }
>     }
>
>
>     @Override
>     protected boolean onLoginFailure(AuthenticationToken token,
> AuthenticationException e, ServletRequest request, ServletResponse
> response)
> {
>         try {
>             WebUtils.issueRedirect(request, response, this.failUrl);
>         } catch (IOException ex) {
>             this.log.error("Error while redirecting to [{}]", this.failUrl,
> ex);
>         }
>         return false;
>     }
>
>
>     abstract protected AuthenticationToken createToken(ServletRequest
> request, ServletResponse response);
> }
>
>
> Token filter
>
> import com.clippitamine.base.utils.QueryStringParser;
> import
> com.clippitamine.security.core.tokens.StringTokenAuthenticationToken;
> import org.apache.shiro.authc.AuthenticationToken;
> import org.apache.shiro.web.util.WebUtils;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
>
> import javax.servlet.ServletRequest;
> import javax.servlet.ServletResponse;
> import javax.servlet.http.HttpServletRequest;
>
>
> public class TokenAuthenticationFilter extends BaseAuthFilter {
>
>
>   private static final Logger LOG =
> LoggerFactory.getLogger(TokenAuthenticationFilter.class);
>
>   @Override
>   protected AuthenticationToken createToken(ServletRequest request,
> ServletResponse response) {
>     HttpServletRequest httpRequest = WebUtils.toHttp(request);
>     String tokenId = null;
>     try {
>       tokenId = QueryStringParser.get(httpRequest.getQueryString(),
> "tokenParamName");
>     } catch (Exception e) {
>       LOG.error("Unable to parse [dcStringToken]", e);
>       return null;
>     }
>     //is client behind something?
>     String ipAddress = httpRequest.getHeader("X-FORWARDED-FOR");
>     if (ipAddress == null) {
>       ipAddress = request.getRemoteAddr();
>     }
>     return new StringTokenAuthenticationToken(tokenId, ipAddress);
>   }
>
>   @Override
>   protected boolean onAccessDenied(ServletRequest request, ServletResponse
> response) throws Exception {
>     AuthenticationToken authenticationToken = this.createToken(request,
> response);
>     if (authenticationToken != null) {
>       return this.executeLogin(authenticationToken, request, response);
>     }
>     return true;
>   }
> }
>
> Token realm (please note that this is spring-boot-hibernate implementation
>
>
> package com.clippitamine.security.hibernate.realms.auth;
>
> /*
>  * Apache License, Version 2.0
>  *
>  * Copyright (c) 2011, Dropchop
>  *
>  * Licensed under the Apache License, Version 2.0 (the "License");
>  * you may not use this file except in compliance with the License.
>  * You may obtain a copy of the License at
>  *
>  *     http://www.apache.org/licenses/LICENSE-2.0
>  *
>  * Unless required by applicable law or agreed to in writing, software
>  * distributed under the License is distributed on an "AS IS" BASIS,
>  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
>  * See the License for the specific language governing permissions and
>  * limitations under the License.
>  */
>
> import com.clippitamine.base.utils.NetUtils;
> import com.clippitamine.entities.core.security.Principal;
> import com.clippitamine.entities.core.security.Token;
> import com.clippitamine.repositories.db.security.PrincipalRepository;
> import com.clippitamine.repositories.db.security.TokenRepository;
> import
> com.clippitamine.security.core.tokens.StringTokenAuthenticationToken;
> import org.apache.shiro.authc.AuthenticationException;
> import org.apache.shiro.authc.AuthenticationInfo;
> import org.apache.shiro.authc.AuthenticationToken;
> import org.apache.shiro.authc.SimpleAuthenticationInfo;
> import org.apache.shiro.authc.credential.CredentialsMatcher;
> import org.springframework.stereotype.Component;
>
> import java.net.InetAddress;
> import java.time.ZonedDateTime;
> import java.util.*;
>
> @Component
> public class TokenRealm extends UsernamePasswordRealm {
>
>     private TokenRepository tokenRepository;
>
>     public TokenRealm(
>             final TokenRepository theTokenRepository,
>             final PrincipalRepository thePrincipalRepository,
>             final CredentialsMatcher theCredentialsMatcher) {
>         super(thePrincipalRepository, theCredentialsMatcher);
>         this.tokenRepository = theTokenRepository;
>
> this.setAuthenticationTokenClass(StringTokenAuthenticationToken.class);
>     }
>
>
>     @Override
>     protected AuthenticationInfo doGetAuthenticationInfo(final
> AuthenticationToken theToken) {
>         StringTokenAuthenticationToken token =
> (StringTokenAuthenticationToken) theToken;
>
>
>         ZonedDateTime now = ZonedDateTime.now();
>         Principal user;
>         Token userToken =
> this.tokenRepository.findById(UUID.fromString((String)
> token.getPrincipal())).orElse(null);
>         if (userToken == null) {
>             throw new AuthenticationException("User token [" +
> token.getPrincipal() + "] not found!");
>         }
>       /*if (!userToken.getName().equals(Token.TokenName.LOGIN.name())) {
>         throw new AuthenticationException("Invalid token [" +
> token.getPrincipal() + "] name [" + userToken.getName() + "] should be [" +
> Token.TokenName.LOGIN.name() + "]!");
>       }*/
>         if (now.isAfter(userToken.getExpiryDate())) {
>             throw new AuthenticationException("User token [" +
> token.getPrincipal() + "] expired!");
>         }
>
>         if (token.hasIp()) {
>             //check IP addresses
>             boolean validIp = false;
>             List<InetAddress> allowedIps = new LinkedList<>();
>             String netMask = userToken.getNetMask();
>             if (netMask != null && !netMask.isEmpty()) {
>                 String[] netIps = netMask.split("\\n");
>                 for (String ip : netIps) {
>                     try {
>                         if (NetUtils.validateIP(token.getIp(), ip)) {
>                             validIp = true;
>                             break;
>                         }
>                     } catch (Exception e) {
>                         this.log.error("Exception when checking user ip
> with
> token IPs", e);
>                     }
>                 }
>                 if (!validIp) {
>                     throw new AuthenticationException("User ip [" +
> token.getIp() + "] is invalid for token [" + token.getPrincipal() + "]");
>                 }
>             }
>         }
>
>         user = userToken.getPrincipal();
>         if (user == null) {
>             throw new AuthenticationException("User with token [" +
> token.getPrincipal() + "] not resolved!");
>         }
>         if (!user.getActive()) {
>             throw new AuthenticationException("User with token [" +
> token.getPrincipal() + "] not active!");
>         }
>         this.log.info("Found user [{}] with username [{}] from token
> [{}]",
> user.getUuid(), token.getPrincipal(), token.getPrincipal());
>         SimpleAuthenticationInfo authenticationInfo = new
> SimpleAuthenticationInfo(user.getUuid(), token.getPrincipal(),
> this.getName());
>         this.clearCache(authenticationInfo.getPrincipals());
>         return authenticationInfo;
>     }
>
> }
>
> Regards
> Armando
>
>
>
> --
> Sent from: http://shiro-user.582556.n2.nabble.com/
>

Reply via email to