この質問は以前に聞かれたことがありますが、ここで特定の問題に直面しています。
Spring Security 3.1.3を使用します。
Webアプリケーションで3つのログインケースがあります。
ケース3)の問題は、ユーザーを「製品」ページにリダイレクトできないことです。ログインに成功すると、それらは何があろうとホームページにリダイレクトされます。
ケース2)では、ログインに成功した後、制限されたページへのリダイレクトがそのまま機能することに注意してください。
Security.xmlファイルの関連部分は次のとおりです。
<!-- Authentication policy for the restricted page -->
<http use-expressions="true" auto-config="true" pattern="/restrictedPage/**">
<form-login login-page="/login/restrictedLogin" authentication-failure-handler-ref="authenticationFailureHandler" />
<intercept-url pattern="/**" access="isAuthenticated()" />
</http>
<!-- Authentication policy for every page -->
<http use-expressions="true" auto-config="true">
<form-login login-page="/login" authentication-failure-handler-ref="authenticationFailureHandler" />
<logout logout-url="/logout" logout-success-url="/" />
</http>
「すべてのページの認証ポリシー」が問題の原因であると思われます。ただし、削除するともうログインできません... j_spring_security_checkは404エラーを送信します。
編集:
ラルフのおかげで、解決策を見つけることができました。だからここにある:私はプロパティを使用しました
<property name="useReferer" value="true"/>
そのラルフは私に見せてくれました。その後、私は私のケースに問題がありました1):ログインページ経由でログインするとき、ユーザーは同じページにとどまりました(そして、以前のようにホームページにリダイレクトされませんでした)。この段階までのコードは次のとおりです。
<!-- Authentication policy for login page -->
<http use-expressions="true" auto-config="true" pattern="/login/**">
<form-login login-page="/login" authentication-success-handler-ref="authenticationSuccessHandlerWithoutReferer" />
</http>
<!-- Authentication policy for every page -->
<http use-expressions="true" auto-config="true">
<form-login login-page="/login" authentication-failure-handler-ref="authenticationFailureHandler" />
<logout logout-url="/logout" logout-success-url="/" authentication-success-handler-ref="authenticationSuccessHandler"/>
</http>
<beans:bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<!-- After login, return to the last visited page -->
<beans:property name="useReferer" value="true" />
</beans:bean>
<beans:bean id="authenticationSuccessHandlerWithoutReferer" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<!-- After login, stay to the same page -->
<beans:property name="useReferer" value="false" />
</beans:bean>
これは少なくとも理論上は機能するはずですが、実際は機能しませんでした。理由はまだわかりませんので、誰かがこれについて答えがあれば、喜んで新しいトピックを作成して、彼のソリューションを共有してください。
それまでの間、私は回避策を見つけました。最良の解決策ではありませんが、私が言ったように、誰かが見せたいものがあれば、私はすべて耳です。これが、ログインページの新しい認証ポリシーです。
<http use-expressions="true" auto-config="true" pattern="/login/**" >
<intercept-url pattern="/**" access="isAnonymous()" />
<access-denied-handler error-page="/"/>
</http>
ここでの解決策は非常に明白です。ログインページは匿名ユーザーにのみ許可されます。ユーザーが接続されると、エラーハンドラーはユーザーをホームページにリダイレクトします。
私はいくつかのテストを行いましたが、すべてがうまく機能しているようです。
ログイン後(ユーザーがリダイレクトされるURL)は、AuthenticationSuccessHandler
によって処理されます。
このインターフェイス(それを実装する具体的なクラスはSavedRequestAwareAuthenticationSuccessHandler
です)は、AbstractAuthenticationProcessingFilter
またはメソッドUsernamePasswordAuthenticationFilter
内の(successfulAuthentication
)のようなサブクラスの1つによって呼び出されます。
したがって、ケース3で他のリダイレクトを行うには、SavedRequestAwareAuthenticationSuccessHandler
をサブクラス化し、必要な処理を行う必要があります。
時々(正確なユースケースに応じて)useReferer
(AbstractAuthenticationTargetUrlRequestHandler
のスーパークラス)によって呼び出されるSimpleUrlAuthenticationSuccessHandler
のSavedRequestAwareAuthenticationSuccessHandler
フラグを有効にすれば十分です。
<bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="filterProcessesUrl" value="/login/j_spring_security_check" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="useReferer" value="true"/>
</bean>
</property>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login?login_error=t" />
</bean>
</property>
</bean>
Olcayのいい答えを拡張したい。彼のアプローチは優れています。リファラーURLをセッションに追加するには、ログインページコントローラーを次のようにする必要があります。
_@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(HttpServletRequest request, Model model) {
String referrer = request.getHeader("Referer");
request.getSession().setAttribute("url_prior_login", referrer);
// some other stuff
return "login";
}
_
そして、SavedRequestAwareAuthenticationSuccessHandler
を拡張し、そのonAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
メソッドをオーバーライドする必要があります。このようなもの:
_public class MyCustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
public MyCustomLoginSuccessHandler(String defaultTargetUrl) {
setDefaultTargetUrl(defaultTargetUrl);
}
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
HttpSession session = request.getSession();
if (session != null) {
String redirectUrl = (String) session.getAttribute("url_prior_login");
if (redirectUrl != null) {
// we do not forget to clean this attribute from session
session.removeAttribute("url_prior_login");
// then we redirect
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
_
次に、Spring構成で、このカスタムクラスをBeanとして定義し、セキュリティ構成で使用する必要があります。 annotation configを使用している場合、次のようになります(WebSecurityConfigurerAdapter
から拡張したクラス):
_@Bean
public AuthenticationSuccessHandler successHandler() {
return new MyCustomLoginSuccessHandler("/yourdefaultsuccessurl");
}
_
configure
メソッド内:
_@Override
protected void configure(HttpSecurity http) throws Exception {
http
// bla bla
.formLogin()
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(successHandler())
.permitAll()
// etc etc
;
}
_
私は次の解決策を持っていますが、それは私のために働きました。
ログインページが要求されるたびに、セッションにリファラー値を書き込みます。
@RequestMapping(value="/login", method = RequestMethod.GET)
public String login(ModelMap model,HttpServletRequest request) {
String referrer = request.getHeader("Referer");
if(referrer!=null){
request.getSession().setAttribute("url_prior_login", referrer);
}
return "user/login";
}
次に、ログインに成功すると、SavedRequestAwareAuthenticationSuccessHandler
のカスタム実装により、ユーザーが前のページにリダイレクトされます。
HttpSession session = request.getSession(false);
if (session != null) {
url = (String) request.getSession().getAttribute("url_prior_login");
}
ユーザーをリダイレクトします。
if (url != null) {
response.sendRedirect(url);
}
私はカスタムOAuth2認可を持っていますが、request.getHeader("Referer")
は決定の時点で利用できません。ただし、セキュリティリクエストは既にExceptionTranslationFilter.handleSpringSecurityException
に保存されています:
protected void sendStartAuthentication(HttpServletRequest request,...
...
requestCache.saveRequest(request, response);
したがって、必要なのは、Spring BeanとしてrequestCache
を共有することだけです。
@Bean
public RequestCache requestCache() {
return new HttpSessionRequestCache();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
...
.requestCache().requestCache(requestCache()).and()
...
}
承認が完了したときに使用します:
@Autowired
private RequestCache requestCache;
public void authenticate(HttpServletRequest req, HttpServletResponse resp){
....
SavedRequest savedRequest = requestCache.getRequest(req, resp);
resp.sendRedirect(savedRequest == null ? "defaultURL" : savedRequest.getRedirectUrl());
}
次の一般的なソリューションは、通常のログイン、Spring Socialログイン、または他のほとんどのSpring Securityフィルターで使用できます。
Spring MVCコントローラーで、製品ページをロードするときに、ユーザーがログインしていない場合はセッションの製品ページへのパスを保存します。XML構成で、デフォルトのターゲットURLを設定します。例えば:
Spring MVCコントローラーでは、リダイレクトメソッドがセッションからパスを読み取り、redirect:<my_saved_product_path>
。
そのため、ユーザーがログインすると、それらは/redirect
page。最後にアクセスした製品ページにすぐにリダイレクトします。
ログインに成功した後、前のページに戻ると、次のカスタム認証マネージャーを使用できます。
<!-- enable use-expressions -->
<http auto-config="true" use-expressions="true">
<!-- src** matches: src/bar.c src/baz.c src/test/bartest.c-->
<intercept-url pattern="/problemSolution/home/**" access="hasRole('ROLE_ADMIN')"/>
<intercept-url pattern="favicon.ico" access="permitAll"/>
<form-login
authentication-success-handler-ref="authenticationSuccessHandler"
always-use-default-target="true"
login-processing-url="/checkUser"
login-page="/problemSolution/index"
default-target-url="/problemSolution/home"
authentication-failure-url="/problemSolution/index?error"
username-parameter="username"
password-parameter="password"/>
<logout logout-url="/problemSolution/logout"
logout-success-url="/problemSolution/index?logout"/>
<!-- enable csrf protection -->
<csrf/>
</http>
<beans:bean id="authenticationSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/problemSolution/home"/>
</beans:bean>
<!-- Select users and user_roles from database -->
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
<password-encoder hash="plaintext">
</password-encoder>
</authentication-provider>
</authentication-manager>
CustomUserDetailsServiceクラス
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
com.codesenior.telif.local.model.User domainUser = userService.getUser(userName);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
domainUser.getUsername(),
domainUser.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getUserRoleList())
);
}
public Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoleList) {
return getGrantedAuthorities(getRoles(userRoleList));
}
public List<String> getRoles(List<UserRole> userRoleList) {
List<String> roles = new ArrayList<String>();
for(UserRole userRole:userRoleList){
roles.add(userRole.getRole());
}
return roles;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
ユーザークラス
import com.codesenior.telif.local.model.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.List;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserService userService;
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
com.codesenior.telif.local.model.User domainUser = userService.getUser(userName);
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
domainUser.getUsername(),
domainUser.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(domainUser.getUserRoleList())
);
}
public Collection<? extends GrantedAuthority> getAuthorities(List<UserRole> userRoleList) {
return getGrantedAuthorities(getRoles(userRoleList));
}
public List<String> getRoles(List<UserRole> userRoleList) {
List<String> roles = new ArrayList<String>();
for(UserRole userRole:userRoleList){
roles.add(userRole.getRole());
}
return roles;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
UserRoleクラス
@Entity
public class UserRole {
@Id
@GeneratedValue
private Integer userRoleId;
private String role;
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "userRoleList")
@JsonIgnore
private List<User> userList;
public Integer getUserRoleId() {
return userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId= userRoleId;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role= role;
}
@Override
public String toString() {
return String.valueOf(userRoleId);
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList= userList;
}
}
SimpleUrlAuthenticationSuccessHandlerを拡張するカスタムSuccessHandlerを使用して、割り当てられた役割に応じてログインするときにユーザーを異なるURLにリダイレクトできます。
CustomSuccessHandlerクラスは、カスタムリダイレクト機能を提供します。
package com.mycompany.uomrmsweb.configuration;
import Java.io.IOException;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
System.out.println("Can't redirect");
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(Authentication authentication) {
String url="";
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> roles = new ArrayList<String>();
for (GrantedAuthority a : authorities) {
roles.add(a.getAuthority());
}
if (isStaff(roles)) {
url = "/staff";
} else if (isAdmin(roles)) {
url = "/admin";
} else if (isStudent(roles)) {
url = "/student";
}else if (isUser(roles)) {
url = "/home";
} else {
url="/Access_Denied";
}
return url;
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
private boolean isUser(List<String> roles) {
if (roles.contains("ROLE_USER")) {
return true;
}
return false;
}
private boolean isStudent(List<String> roles) {
if (roles.contains("ROLE_Student")) {
return true;
}
return false;
}
private boolean isAdmin(List<String> roles) {
if (roles.contains("ROLE_SystemAdmin") || roles.contains("ROLE_ExaminationsStaff")) {
return true;
}
return false;
}
private boolean isStaff(List<String> roles) {
if (roles.contains("ROLE_AcademicStaff") || roles.contains("ROLE_UniversityAdmin")) {
return true;
}
return false;
}
}
Spring SimpleUrlAuthenticationSuccessHandlerクラスを拡張し、ユーザー定義の決定ターゲットUrl()メソッドによって返されたURLで構成済みのRedirectStrategy [この場合はデフォルト]を使用してリダイレクトを単に呼び出すhandle()メソッドをオーバーライドします。このメソッドは、現在ログインしているユーザーのロールを認証オブジェクトから抽出し、そのロールに基づいて適切なURLを構築します。最後に、RedirectStrategyは、Spring Securityフレームワーク内のすべてのリダイレクトを担当し、リクエストを指定されたURLにリダイレクトします。
SecurityConfigurationクラスを使用してCustomSuccessHandlerを登録します。
package com.mycompany.uomrmsweb.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
@Autowired
CustomSuccessHandler customSuccessHandler;
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").access("hasRole('USER')")
.antMatchers("/admin/**").access("hasRole('SystemAdmin') or hasRole('ExaminationsStaff')")
.antMatchers("/staff/**").access("hasRole('AcademicStaff') or hasRole('UniversityAdmin')")
.antMatchers("/student/**").access("hasRole('Student')")
.and().formLogin().loginPage("/login").successHandler(customSuccessHandler)
.usernameParameter("username").passwordParameter("password")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
successHandlerは、任意のカスタムロジックに基づく最終的なリダイレクトを担当するクラスです。この場合、ユーザーの役割[USER/Student/SystemAdmin/UniversityAdmin/ExaminationsStaff/AcademicStaff]に基づいてユーザーを[student/admin/staff]にリダイレクトします。
tkuÖzdemirのソリューション はある程度機能しますが、セッション属性が優先されるため、保存されたリクエストの目的をある程度無効にします。これは、セキュアページへのリダイレクトが意図したとおりに機能しないことを意味します。ログイン後、リダイレクトターゲットではなく、アクセスしていたページに送信されます。そのため、別の方法として、SavedRequestAwareAuthenticationSuccessHandlerを拡張する代わりに変更したバージョンを使用できます。これにより、セッション属性をいつ使用するかをより適切に制御できるようになります。
以下に例を示します。
private static class MyCustomLoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RequestCache requestCache = new HttpSessionRequestCache();
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
HttpSession session = request.getSession();
if (session != null) {
String redirectUrl = (String) session.getAttribute("url_prior_login");
if (redirectUrl != null) {
session.removeAttribute("url_prior_login");
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
|| (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
clearAuthenticationAttributes(request);
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
}
また、認証が失敗した場合、リファラーはログインページそのものになるため、リファラーを保存する必要はありません。そのため、エラーパラメータを手動で確認するか、以下のように別のRequestMappingを提供します。
@RequestMapping(value = "/login", params = "error")
public String loginError() {
// Don't save referrer here!
}