Voici ma config de sécurité de printemps:
<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>
La authenticationSuccessHandler
étend la SavedRequestAwareAuthenticationSuccessHandler
en s'assurant que l'utilisateur est redirigé vers la page qu'il a demandée à l'origine.
Cependant, étant donné que /auth/login
est marqué comme security="none"
, je ne parviens pas à rediriger correctement l'utilisateur vers la page d'accueil s'il accède à la page de connexion après s'être connecté. Je pense que c'est la bonne expérience utilisateur aussi.
J'ai aussi essayé ci-dessous, mais l'objet Principal
est toujours null
, probablement à nouveau à cause de l'attribut security="none"
.
@RequestMapping(value = "/auth/login", method = GET)
public String showLoginForm(HttpServletRequest request, Principal principal) {
if(principal != null) {
return "redirect:/";
}
return "login";
}
J'ai vérifié le sujet plus en profondeur que la dernière fois et j'ai constaté que vous devez déterminer si l'utilisateur est authentifié par vous-même dans le contrôleur. Row Winch (Spring Security dev) dit ici :
Spring Security n’a pas connaissance des éléments internes de votre application (c’est-à-dire si vous souhaitez assouplir votre page de connexion si l’utilisateur est connecté ou non). Pour afficher votre page d'accueil lorsque la page de connexion est demandé et que l'utilisateur est connecté, utilisez la variable
SecurityContextHolder
dans la page de connexion (ou son contrôleur) et rediriger ou transférer l'utilisateur vers la page d'accueil.
La solution serait donc de déterminer si l'utilisateur demandant /auth/login
est anonyme ou non, comme ci-dessous.
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>
Ajoutez à la définition du bean applicationContext.xml:
<bean id="securityContextAccessor"
class="com.example.spring.security.SecurityContextAccessorImpl" />
qui est classe
public final class SecurityContextAccessorImpl
implements SecurityContextAccessor {
@Autowired
private AuthenticationTrustResolver authenticationTrustResolver;
@Override
public boolean isCurrentAuthenticationAnonymous() {
final Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
return authenticationTrustResolver.isAnonymous(authentication);
}
}
mise en place d'une interface simple
public interface SecurityContextAccessor {
boolean isCurrentAuthenticationAnonymous();
}
( SecurityContextHolder
code d'accès est découplé du contrôleur, j'ai suivi la suggestion de cette réponse , d'où SecurityContextAccessor
interface.)
Et dernier mais non le moindre, rediriger la logique dans le contrôleur:
@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;
}
}
}
Définir defaultTargetUrl
String bean semble être un hack, mais je n'ai pas de meilleur moyen de ne pas coder en dur l'url ... (En fait, dans notre projet, nous utilisons <util:constant>
avec une classe contenant des champs String statiques.) après tout.
Vous pouvez également limiter votre page de connexion à ROLE_ANONYMOUS
et définir un <access-denied-handler />
:
<access-denied-handler ref="accessDeniedHandler" />
<intercept-url pattern="/auth/login" access="ROLE_ANONYMOUS" />
Et dans votre gestionnaire, vérifiez si l'utilisateur est déjà authentifié:
@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);
}
}
Implémentez un intercepteur de redirection à cette fin:
L'intercepteur (implémentant HandlerInterceptor
interface) vérifie si quelqu'un tente d'accéder à la page de connexion et si cette personne est déjà connectée, l'intercepteur envoie une redirection vers la page d'index.
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;
}
}
Vous pouvez conserver le flux simple par l'attribut access-denied-page
dans l'élément http
ou sous la forme dtrunk dit d'écrire gestionnaire pour l'accès refusé ainsi que. la config serait comme
<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>
dans le contrôleur pour /403
@RequestMapping(value = "/403", method = RequestMethod.GET)
public String accessDenied() { //simple impl
return "redirect:/home";
}
et pour /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;
}
et lorsque vous faites une demande pour /admin/dashboard
sans être connecté, il redirigera /login
automatiquement par sécurité
<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>