Ich habe eine Spring-MVC-Anwendung. Sie verwendet eine eigene benutzerdefinierte Anmeldeseite . Bei erfolgreicher Anmeldung wird ein 'LOGGED_IN_USER' Objekt in die HTTPSession gestellt.
Ich möchte nur authentifizierten Benutzern den Zugriff auf URLs erlauben. Ich weiß, dass ich dies mit einem Webfilter erreichen kann. Aber diesen Teil möchte ich mit Spring Security machen (mein Check bleibt derselbe - suchen Sie in HTTPSession nach dem 'LOGGED_IN_USER'-Objekt, falls Sie angemeldet sind).
Meine Einschränkung ist, dass ich das Login-Verhalten derzeit nicht ändern kann - Spring Security wird noch nicht verwendet.
Welchen Aspekt von Spring Security kann ich verwenden, um diesen Teil alleine zu erreichen? Überprüfen Sie, ob die Anforderung authentifiziert ist (vom angemeldeten Benutzer).
Es gibt mindestens 4 verschiedene Möglichkeiten:
dies ist der einfachste Weg
<security:http auto-config="true" use-expressions="true" ...>
...
<security:intercept-url pattern="/forAll/**" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
erfordert <global-method-security secured-annotations="enabled" />
@Secured("ROLE_ADMIN")
@RequestMapping(params = "onlyForAdmins")
public ModelAndView onlyForAdmins() {
....
}
erfordert <global-method-security pre-post-annotations="enabled" />
@PreAuthorize("isAuthenticated()")
@RequestMapping(params = "onlyForAuthenticated")
public ModelAndView onlyForAuthenticatedUsers() {
....
}
SecurityContextHolder.getContext().getAuthentication() != null &&
SecurityContextHolder.getContext().getAuthentication().isAuthenticated() &&
//when Anonymous Authentication is enabled
!(SecurityContextHolder.getContext().getAuthentication()
instanceof AnonymousAuthenticationToken)
Wenn die integrierten Ausdrücke nicht ausreichen, können Sie sie erweitern. Wie Sie die SpEL-Ausdrücke für die Methodenanmerkungen erweitern, wird hier zum Beispiel erläutert:
Für den Interceptor <security:intercept-url ... access="myCustomAuthenticatedExpression" />
ist jedoch ein etwas anderer Ansatz möglich, der sich nicht mit dem Problem der privaten Klasse befassen muss. - Ich habe es nur für Spring Security 3.0 gemacht, aber ich hoffe, dass es auch für 3.1 funktioniert.
1.) Sie müssen eine neue Klasse erstellen, die sich von WebSecurityExpressionRoot
erstreckt (Präfix-Web ist der wichtige Teil!).
public class MyCustomWebSecurityExpressionRoot
extends WebSecurityExpressionRoot {
public MyCustomWebSecurityExpressionRoot(Authentication a,
FilterInvocation f) {
super(a, f);
}
/** That method is the one that does the expression evaluation! */
public boolean myCustomAuthenticatedExpression() {
return super.request.getSession().getValue("myFlag") != null;
}
}
2.) Sie müssen die Variable DefaultWebSecurityExpressionRootHandler
erweitern, um einen Handler zu haben, der Ihren benutzerdefinierten Ausdruckswurzel bereitstellt
public class MyCustomWebSecurityExpressionHandler
extends DefaultWebSecurityExpressionHandler {
@Override
public EvaluationContext createEvaluationContext(Authentication a,
FilterInvocation f) {
StandardEvaluationContext ctx =
(StandardEvaluationContext) super.createEvaluationContext(a, f);
WebSecurityExpressionRoot myRoot =
new MyCustomWebSecurityExpressionRoot(a, f);
ctx.setRootObject(myRoot);
return ctx;
}
}
3.) Dann müssen Sie Ihren Handler bei den Wählern registrieren
<security:http use-expressions="true"
access-decision-manager-ref="httpAccessDecisionManager" ...>
...
<security:intercept-url pattern="/restricted/**"
access="myCustomAuthenticatedExpression" />
...
</security:http>
<bean id="httpAccessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<constructor-arg name="decisionVoters">
<list>
<ref bean="webExpressionVoter" />
</list>
</constructor-arg>
</bean>
<bean id="webExpressionVoter"
class="org.springframework.security.web.access.expression.WebExpressionVoter">
<property name="expressionHandler"
ref="myCustomWebSecurityExpressionHandler" />
</bean>
<bean id="myCustomWebSecurityExpressionHandler"
class="MyCustomWebSecurityExpressionHandler" />
Spring Security 3.1 Update
Seit Spring Security 3.1 ist es etwas einfacher, einen benutzerdefinierten Ausdruck zu implementieren. Man muss nicht länger WebSecurityExpressionHandler
sublcass machen und createEvaluationContext
überschreiben. Stattdessen eine Unterklasse AbstractSecurityExpressionHandler<FilterInvocation>
oder ihre Unterklasse DefaultWebSecurityExpressionHandler
und überschreiben SecurityExpressionOperations createSecurityExpressionRoot(final Authentication a, final FilterInvocation f)
.
public class MyCustomWebSecurityExpressionHandler
extends DefaultWebSecurityExpressionHandler {
@Override
public SecurityExpressionOperations createSecurityExpressionRoot(
Authentication a,
FilterInvocation f) {
WebSecurityExpressionRoot myRoot =
new MyCustomWebSecurityExpressionRoot(a, f);
myRoot.setPermissionEvaluator(getPermissionEvaluator());
myRoot.setTrustResolver(this.trustResolver);
myRoot.setRoleHierarchy(getRoleHierarchy());
return myRoot;
}
}
Eine andere Lösung können Sie Klassen erstellen:
public class AuthenticationSystem {
public static boolean isLogged() {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return null != authentication && !("anonymousUser").equals(authentication.getName());
}
// ...
// Any another methods, for example, logout
}
Dann im Controller:
@Controller
@RequestMapping(value = "/promotion")
public final class PromotionController {
@RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
public final String root() {
if (!AuthenticationSystem.isLogged()) return "login"; // or some logic
// some logic
return "promotion/index";
}
}
PS:
Vorherige Lösung hat ein Problem, das Peter in Kommentaren erklärt.
@Controller
@RequestMapping(value = "/promotion")
public final class PromotionController {
@RequestMapping(value = {"", "/"}, method = RequestMethod.GET)
public final String root(final Principal principal) {
if (null == principal) return "login"; // or some logic
// some logic
return "promotion/index";
}
}
Ist es das, was Sie erreichen wollen?
<c:choose>
<c:when test="${pageContext.request.userPrincipal.authenticated}">Show something</c:when>
<c:otherwise>Show something else</c:otherwise>
</c:choose>
Viele Authentifizierungsanbieter erstellen ein UserDetails-Objekt als Prinzipal.
Eine andere Methode, die ich gefunden habe - mithilfe von Spring-Security - besteht darin, zu prüfen, ob der Rückgabewert von Authentication.getPrincipal()
eine Instanz von UserDetails
ist. Die Methode gibt standardmäßig "anonymousUser"
(String
) zurück.
boolean isUserLoggedIn(){
return SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserDetails
}