我有一个Spring MVC应用程序。它使用自己的自定义登录页面。成功登录后,即可获得“LOGGED_IN_USER”' object放在HTTPSession中。
我想只允许经过身份验证的用户访问网址。我知道我可以通过使用网络过滤器实现这一目标。但是,这部分我想使用Spring Security(我的检查将保持不变 - 在HTTPSession中查找' LOGGED_IN_USER'对象,如果存在,则登录)。
我的约束是我目前无法更改登录行为 - 尚未使用Spring Security。
我可以使用Spring Security的哪个方面单独实现此部分 - 检查请求是否经过身份验证(来自登录用户)?
答案 0 :(得分:108)
至少有4种不同的方式:
这是最简单的方法
<security:http auto-config="true" use-expressions="true" ...>
...
<security:intercept-url pattern="/forAll/**" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
需要<global-method-security secured-annotations="enabled" />
@Secured("ROLE_ADMIN")
@RequestMapping(params = "onlyForAdmins")
public ModelAndView onlyForAdmins() {
....
}
需要<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)
如果内置表达式不够,可以扩展它们。如何扩展方法注释的SpEL表达式,例如:
但是对于拦截器<security:intercept-url ... access="myCustomAuthenticatedExpression" />
,可能存在一种稍微不同的方法,它不需要处理私有类问题。 - 我只为Spring Security 3.0做过,但我希望它也适用于3.1。
1。)你需要创建一个从WebSecurityExpressionRoot
扩展的新类(前缀Web是重要的部分!)。
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。)你需要扩展DefaultWebSecurityExpressionRootHandler
以拥有一个提供自定义表达式根目录的处理程序
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.)然后你需要向选民登记你的处理程序
<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更新
从Spring Security 3.1开始,实现自定义表达式要容易一些。一个人不再需要升级WebSecurityExpressionHandler
并覆盖createEvaluationContext
。而是一个子类AbstractSecurityExpressionHandler<FilterInvocation>
或其子类DefaultWebSecurityExpressionHandler
并覆盖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;
}
}
答案 1 :(得分:14)
另一种解决方案,你可以创建类:
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
}
然后,在控制器中:
@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 强>:
以前的解决方案有一个问题,可以在评论中解释彼得。
@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";
}
}
答案 2 :(得分:9)
这是你想要实现的目标吗?
<c:choose>
<c:when test="${pageContext.request.userPrincipal.authenticated}">Show something</c:when>
<c:otherwise>Show something else</c:otherwise>
</c:choose>
答案 3 :(得分:0)
许多身份验证提供程序将创建一个UserDetails对象作为主体。
我发现的另一种方法-使用spring-security-是检查Authentication.getPrincipal()
的返回值是否是UserDetails
的实例;该方法默认返回"anonymousUser"
(String
)。
boolean isUserLoggedIn(){
return SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserDetails
}
答案 4 :(得分:0)
您可以简单地创建一个扩展 WebSecurityConfigurerAdapter 的类 并在匹配的网址
中添加已认证@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/User/**").authenticated()
}
你可以走了