18

这是我的弹簧安全配置:

<http pattern="/auth/login" security="none" />
<http pattern="/auth/loginFailed" security="none" />
<http pattern="/resources/**" security="none" />

<http auto-config="true" access-decision-manager-ref="accessDecisionManager">
    <intercept-url pattern="/auth/logout" access="permitAll"/>
    <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS"/>
    <intercept-url pattern="/**" access="XYZ_ACCESS"/>

    <form-login
        login-page="/auth/login"
        authentication-failure-url="/auth/loginFailed"
        authentication-success-handler-ref="authenticationSuccessHandler" />
    <logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>

authenticationSuccessHandler扩展了SavedRequestAwareAuthenticationSuccessHandler确保用户被重定向到他最初请求的页面。

但是,由于/auth/login标记为security="none",如果用户在登录后访问登录页面,我无法成功将用户重定向到主页。我相信这也是正确的用户体验

我也尝试了以下方法,但Principal对象总是null,大概是因为该security="none"属性。

@RequestMapping(value = "/auth/login", method = GET)
public String showLoginForm(HttpServletRequest request, Principal principal) {
    if(principal != null) {
        return "redirect:/";
    }

    return "login";
}
4

6 回答 6

9

我比上次更深入地检查了该主题,发现您必须确定用户是否在控制器中由您自己进行身份验证。Row Winch(Spring Security 开发人员)在这里说

Spring Security 不知道您的应用程序的内部结构(即,如果您想根据用户是否登录来使您的登录页面灵活)。要在请求登录页面并且用户登录时显示您的主页,请使用SecurityContextHolder登录页面(或其控制器)并将用户重定向或转发到主页。

因此,解决方案将确定用户请求/auth/login是否是匿名的,如下所示。

applicationContext-security.xml

<http auto-config="true" use-expressions="true"
        access-decision-manager-ref="accessDecisionManager">
    <intercept-url pattern="/auth/login" access="permitAll" />
    <intercept-url pattern="/auth/logout" access="permitAll" />
    <intercept-url pattern="/admin/**" access="ADMINISTRATIVE_ACCESS" />
    <intercept-url pattern="/**" access="XYZ_ACCESS" />

    <form-login login-page="/auth/login"
        authentication-failure-url="/auth/loginFailed"
        authentication-success-handler-ref="authenticationSuccessHandler" />
    <logout logout-url="/auth/logout" logout-success-url="/auth/login" />
</http>

<beans:bean id="defaultTargetUrl" class="java.lang.String">
    <beans:constructor-arg value="/content" />
</beans:bean>

<beans:bean id="authenticationTrustResolver"
        class="org.springframework.security.authentication.AuthenticationTrustResolverImpl" />

<beans:bean id="authenticationSuccessHandler"
        class="com.example.spring.security.MyAuthenticationSuccessHandler">
    <beans:property name="defaultTargetUrl" ref="defaultTargetUrl" />
</beans:bean>

添加到applicationContext.xml bean 定义:

<bean id="securityContextAccessor"
    class="com.example.spring.security.SecurityContextAccessorImpl" />

这是类

public final class SecurityContextAccessorImpl
      implements SecurityContextAccessor {

  @Autowired
  private AuthenticationTrustResolver authenticationTrustResolver;

  @Override
  public boolean isCurrentAuthenticationAnonymous() {
    final Authentication authentication =
        SecurityContextHolder.getContext().getAuthentication();
    return authenticationTrustResolver.isAnonymous(authentication);
  }
}

实现简单的接口

public interface SecurityContextAccessor {
  boolean isCurrentAuthenticationAnonymous();
}

SecurityContextHolder访问代码与控制器分离,我遵循了这个答案的建议,因此是SecurityContextAccessor接口。)

最后但并非最不重要的重定向控制器中的逻辑:

@Controller
@RequestMapping("/auth")
public class AuthController {
  @Autowired
  SecurityContextAccessor securityContextAccessor;

  @Autowired
  @Qualifier("defaultTargetUrl")
  private String defaultTargetUrl;

  @RequestMapping(value = "/login", method = RequestMethod.GET)
  public String login() {
    if (securityContextAccessor.isCurrentAuthenticationAnonymous()) {
      return "login";
    } else {
      return "redirect:" + defaultTargetUrl;
    }
  }
}

定义defaultTargetUrlString bean 似乎是一个 hack,但我没有更好的方法不硬编码 url...(实际上在我们的项目中,我们使用<util:constant>包含静态 final String 字段的类。)但它毕竟有效。

于 2012-09-26T13:04:12.150 回答
7

您还可以将登录页面限制为ROLE_ANONYMOUS并设置<access-denied-handler />

<access-denied-handler ref="accessDeniedHandler" />
<intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" />

并在您的处理程序中检查用户是否已通过身份验证:

@Service
public class AccessDeniedHandler extends AccessDeniedHandlerImpl {
    private final String HOME_PAGE = "/index.html";

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null && !(auth instanceof AnonymousAuthenticationToken)) {
            response.sendRedirect(HOME_PAGE);
        }

        super.handle(request, response, e);
    }
}
于 2015-01-13T14:33:28.190 回答
4

为此目的实现一个重定向拦截器:

拦截器(实现HandlerInterceptor接口)检查是否有人尝试访问登录页面,如果此人已经登录,则拦截器发送重定向到索引页面。

public class LoginPageRedirectInterceptor extends HandlerInterceptorAdapter {    

    private String[] loginPagePrefixes = new String[] { "/login" };

    private String redirectUrl = "/index.html";

    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        if (isInLoginPaths(this.urlPathHelper.getLookupPathForRequest(request))
                           && isAuthenticated()) {
            response.setContentType("text/plain");
            sendRedirect(request, response);
            return false;
        } else {
            return true;
        }
    }

    private boolean isAuthenticated() {
        Authentication authentication =
                        SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            return false;
        }
        if (authentication instanceof AnonymousAuthenticationToken) {
            return false;
        }
        return authentication.isAuthenticated();
    }

    private void sendRedirect(HttpServletRequest request,
                              HttpServletResponse response) {

        String encodedRedirectURL = response.encodeRedirectURL(
                                 request.getContextPath() + this.redirectUrl);
        response.setStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
        response.setHeader("Location", encodedRedirectURL);
    }

    private boolean isInLoginPaths(final String requestUrl) {   
        for (String login : this.loginPagePrefixes) {
            if (requestUrl.startsWith(login)) {
                return true;
            }
        }
        return false;
    }
}
于 2013-08-02T08:21:12.270 回答
0
<http pattern="/login" auto-config="true" disable-url-rewriting="true">
  <intercept-url pattern="/login" access="ROLE_ANONYMOUS"/>
  <access-denied-handler error-page="/index.jsp"/>
</http>
于 2016-03-07T20:04:11.837 回答
0

您可以通过元素中的access-denied-page属性保持简单流程,或者像dtrunk所说的那样编写拒绝访问的处理程序。配置就像http

<http access-denied-page="/403" ... >
 <intercept-url pattern="/login" access="ROLE_ANONYMOUS" />
 <intercept-url pattern="/user/**" access="ROLE_USER" />
 <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
 <form-login login-page="/login" default-target-url="/home" ... />
 ...
</http>

在控制器中/403

@RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied() { //simple impl
    return "redirect:/home";
}

并且对于/home

@RequestMapping(value = "/home", method = RequestMethod.GET)
public String home(Authentication authentication) {
 // map as many home urls with Role
    Map<String, String> dashBoardUrls = new HashMap<String, String>();
    dashBoardUrls.put("ROLE_USER", "/user/dashboard");
    dashBoardUrls.put("ROLE_ADMIN", "/admin/dashboard");

    String url = null;

    Collection<? extends GrantedAuthority> grants = authentication
            .getAuthorities();
 // for one role per user
    for (GrantedAuthority grantedAuthority : grants) {
        url = dashBoardUrls.get(grantedAuthority.getAuthority());
    }
    if (url == null)
        return "/errors/default_access_denied.jsp";

    return "redirect:" + url;
}

当您在未登录的情况下发出请求时/admin/dashboard,它将/login由安全自动重定向

于 2016-02-04T06:45:38.070 回答
-1

您可以尝试检查

if(SecurityContextHolder.getContext().getAuthentication() == null)

True 表示用户未通过身份验证,因此可以发送到登录页面。我不知道这是多么强大/可靠,但尝试似乎是合理的。

于 2014-10-30T22:20:21.377 回答