Spring Security Sessions without cookies

后端 未结 4 2069
天命终不由人
天命终不由人 2020-12-06 04:24

I\'m trying to manage sessions in Spring Security without leveraging cookies. The reasoning is - our application is displayed within an iframe from another domain, we need t

相关标签:
4条回答
  • I appreciate all the answers above - I ended up opting for an easier solution without making any application-level changes because the owner of domainA.com was willing to work with us. Posting it here for others, as I didn't even think of this originally...

    Basically :

    • Owner of domainA.com created a DNS record to for domainB.domainA.com -> domainB.com
    • Owner of domainB.com (me) requested a public SSL certificate for domainB.domainA.com via "email validation" (I did this through AWS, but I'm sure there are other mechanisms via othe providers)
    • The above request was sent to the webmasters of domainA.com -> they approved and issued the public certificate
    • Once issued - I was able to configure my application (or load balancer) to use this new certificate, and they configured their application to point to "domainB.domainA.com" (which subsequently routed to domainB.com in DNS)
    • Now, the browsers issue cookies for domainB.domainA.com and since they are the same primary domain, the cookies get created without any work-arounds needed.

    Thanks again for the answers, apologies for not selecting an answer here - busy week.

    0 讨论(0)
  • 2020-12-06 04:53

    Have you looked at Spring Session: HttpSession & RestfulAPI which uses HTTP headers instead of cookies. See the REST sample projects in REST Sample.

    0 讨论(0)
  • 2020-12-06 04:59

    Form based logins are mainly stateful sessions. In your scenario using stateless sessions would be best.

    JWT provide implementation for this. Its basically a key which you need to pass as header in each HTTP request. So as long as you have the key. API is available.

    We can integrate JWT with Spring.

    Basically you need to write these logic.

    • Generate Key Logic
    • Use JWT in Spring Security
    • Validate key on each call

    I can give you a head start

    pom.xml

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.0</version>
    </dependency>
    

    TokenHelper.java

    Contain useful functions for validating, checking, and parsing Token.

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Component;
    
    import com.test.dfx.common.TimeProvider;
    import com.test.dfx.model.LicenseDetail;
    import com.test.dfx.model.User;
    
    
    @Component
    public class TokenHelper {
    
        protected final Log LOGGER = LogFactory.getLog(getClass());
    
        @Value("${app.name}")
        private String APP_NAME;
    
        @Value("${jwt.secret}")
        public String SECRET;    //  Secret key used to generate Key. Am getting it from propertyfile
    
        @Value("${jwt.expires_in}")
        private int EXPIRES_IN;  //  can specify time for token to expire. 
    
        @Value("${jwt.header}")
        private String AUTH_HEADER;
    
    
        @Autowired
        TimeProvider timeProvider;
    
        private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;  // JWT Algorithm for encryption
    
    
        public Date getIssuedAtDateFromToken(String token) {
            Date issueAt;
            try {
                final Claims claims = this.getAllClaimsFromToken(token);
                issueAt = claims.getIssuedAt();
            } catch (Exception e) {
                LOGGER.error("Could not get IssuedDate from passed token");
                issueAt = null;
            }
            return issueAt;
        }
    
        public String getAudienceFromToken(String token) {
            String audience;
            try {
                final Claims claims = this.getAllClaimsFromToken(token);
                audience = claims.getAudience();
            } catch (Exception e) {
                LOGGER.error("Could not get Audience from passed token");
                audience = null;
            }
            return audience;
        }
    
        public String refreshToken(String token) {
            String refreshedToken;
            Date a = timeProvider.now();
            try {
                final Claims claims = this.getAllClaimsFromToken(token);
                claims.setIssuedAt(a);
                refreshedToken = Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(generateExpirationDate())
                    .signWith( SIGNATURE_ALGORITHM, SECRET )
                    .compact();
            } catch (Exception e) {
                LOGGER.error("Could not generate Refresh Token from passed token");
                refreshedToken = null;
            }
            return refreshedToken;
        }
    
        public String generateToken(String username) {
            String audience = generateAudience();
            return Jwts.builder()
                    .setIssuer( APP_NAME )
                    .setSubject(username)
                    .setAudience(audience)
                    .setIssuedAt(timeProvider.now())
                    .setExpiration(generateExpirationDate())
                    .signWith( SIGNATURE_ALGORITHM, SECRET )
                    .compact();
        }
    
    
    
        private Claims getAllClaimsFromToken(String token) {
            Claims claims;
            try {
                claims = Jwts.parser()
                        .setSigningKey(SECRET)
                        .parseClaimsJws(token)
                        .getBody();
            } catch (Exception e) {
                LOGGER.error("Could not get all claims Token from passed token");
                claims = null;
            }
            return claims;
        }
    
        private Date generateExpirationDate() {
            long expiresIn = EXPIRES_IN;
            return new Date(timeProvider.now().getTime() + expiresIn * 1000);
        }
    
        public int getExpiredIn() {
            return EXPIRES_IN;
        }
    
        public Boolean validateToken(String token, UserDetails userDetails) {
            User user = (User) userDetails;
            final String username = getUsernameFromToken(token);
            final Date created = getIssuedAtDateFromToken(token);
            return (
                    username != null &&
                    username.equals(userDetails.getUsername()) &&
                            !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate())
            );
        }
    
        private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
            return (lastPasswordReset != null && created.before(lastPasswordReset));
        }
    
        public String getToken( HttpServletRequest request ) {
            /**
             *  Getting the token from Authentication header
             *  e.g Bearer your_token
             */
            String authHeader = getAuthHeaderFromHeader( request );
            if ( authHeader != null && authHeader.startsWith("Bearer ")) {
                return authHeader.substring(7);
            }
    
            return null;
        }
    
        public String getAuthHeaderFromHeader( HttpServletRequest request ) {
            return request.getHeader(AUTH_HEADER);
        }
    
    
    }
    

    WebSecurity

    SpringSecurity Logic to add JWT check

    @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ).and()
            .exceptionHandling().authenticationEntryPoint( restAuthenticationEntryPoint ).and()
            .authorizeRequests()
            .antMatchers("/auth/**").permitAll()
            .antMatchers("/login").permitAll()
            .antMatchers("/home").permitAll()
            .antMatchers("/actuator/**").permitAll()
            .anyRequest().authenticated().and()
            .addFilterBefore(new TokenAuthenticationFilter(tokenHelper, jwtUserDetailsService), BasicAuthenticationFilter.class);
    
            http.csrf().disable();
        }
    

    TokenAuthenticationFilter.java

    Check each Rest Call for valid Token

    package com.test.dfx.security;
    
    import java.io.IOException;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    public class TokenAuthenticationFilter extends OncePerRequestFilter {
    
        protected final Log logger = LogFactory.getLog(getClass());
    
        private TokenHelper tokenHelper;
    
        private UserDetailsService userDetailsService;
    
        public TokenAuthenticationFilter(TokenHelper tokenHelper, UserDetailsService userDetailsService) {
            this.tokenHelper = tokenHelper;
            this.userDetailsService = userDetailsService;
        }
    
    
        @Override
        public void doFilterInternal(
                HttpServletRequest request,
                HttpServletResponse response,
                FilterChain chain
        ) throws IOException, ServletException {
    
            String username;
            String authToken = tokenHelper.getToken(request);
    
            logger.info("AuthToken: "+authToken);
    
            if (authToken != null) {
                // get username from token
                username = tokenHelper.getUsernameFromToken(authToken);
                logger.info("UserName: "+username);
                if (username != null) {
                    // get user
                    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                    if (tokenHelper.validateToken(authToken, userDetails)) {
                        // create authentication
                        TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails);
                        authentication.setToken(authToken);
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
                }else{
                    logger.error("Something is wrong with Token.");
                }
            }
            chain.doFilter(request, response);
        }
    
    
    }
    
    0 讨论(0)
  • 2020-12-06 05:03

    You can have a token based communication between the site DomainB.com server and the client browser. The token can be sent from the DomainB.com server in the response's header , after authentication. The client browser can then save the token in localstorage/session storage (have a expiry time too). The client can then send the token in every request's header. Hope this helps.

    0 讨论(0)
提交回复
热议问题