Cookieを利用せずにSpring Securityでセッションを管理しようとしています。その理由は、アプリケーションが別のドメインのiframe内に表示されるため、アプリケーションでセッションを管理する必要があるため 、SafariはクロスドメインCookieの作成を制限するためです 。 (コンテキスト:domainA.comはiframeにdomainB.comを表示します。domainB.comはdomainB.comで利用するためにJSESSIONID Cookieを設定しますが、ユーザーのブラウザにdomainA.comが表示されるため、SafariはdomainB.comによるCookieの作成を制限します) 。
これを達成するために私が考えることができる唯一の方法(OWASPセキュリティの推奨事項に対して)-GETパラメーターとしてURLにJSESSIONIDを含めることです。私はこれをしたくありませんが、私は代替案を考えることができません。
したがって、この質問は両方についてです:
これに関するSpringのドキュメントを確認し、 enableSessionUrlRewriting を使用してこれを許可する必要があります
だから私はこれをやった:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.enableSessionUrlRewriting(true)
これはJSESSIONIDをURLに追加しませんでしたが、今は許可する必要があります。次に、この質問で見つかったコードを活用して、「トラッキングモード」をURLに設定しました
@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext
.setSessionTrackingModes(
Collections.singleton(SessionTrackingMode.URL)
);
この後でも-アプリケーションは、URLではなくCookieとしてJSESSIONIDを追加します。
誰かが私をここで正しい方向に向けるのを手伝ってもらえますか?
Spring Session:HttpSession&RestfulAPI を見て、Cookieの代わりにHTTPヘッダーを使用しています。 REST Sample のRESTサンプルプロジェクトを参照してください。
フォームベースのログインは、主にステートフルセッションです。シナリオでは、ステートレスセッションを使用するのが最適です。
[〜#〜] jwt [〜#〜] この実装を提供します。基本的に、各HTTPリクエストのヘッダーとして渡す必要があるキーです。あなたが鍵を持っている限り。 APIが利用可能です。
JWTとSpringを統合できます。
基本的に、これらのロジックを記述する必要があります。
有利なスタートを切ることができます
pom.xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
TokenHelper.Java
トークンの検証、チェック、解析に役立つ関数が含まれています。
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
JWTチェックを追加するSpringSecurityロジック
@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
有効なトークンがないか、各レストコールを確認してください
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);
}
}
サイトDomainB.comサーバーとクライアントブラウザーの間でトークンベースの通信を行うことができます。トークンは、認証後にDomainB.comサーバーから応答のヘッダーで送信できます。クライアントブラウザは、トークンをローカルストレージ/セッションストレージに保存できます(有効期限もあります)。その後、クライアントはすべてのリクエストのヘッダーでトークンを送信できます。お役に立てれば。
上記のすべての回答に感謝します。domainA.comの所有者が喜んで協力してくれたため、アプリケーションレベルの変更を行わずに簡単なソリューションを選択することになりました。もともとこれも考えていなかったので、他の人のためにここに投稿します...
基本的に :
回答をありがとう、ここで回答を選択しなかったことをおaびします-忙しい週。