让我们说我有一个带有Spring Security的Spring MVC。我有一个控制器方法,我想得到我祖母的食谱。
RequestMapping(value="/Recipe/{recipeId}", method = RequestMethod.GET)
Public Recipe getRecipe(@PathVariable("recipeId") int recepieId) {
Recipe recipe = database.getRecipeById(recepieId);
if(recipe.isBisket()) {
return recipe;
}
if(recipe.isSecretCookieRecipe()) {
boolean isAuthenitacted = Utils.authenticatUser(user);
if(isAuthenticated() {
return recipe;
} else {
// do something to authenitcate and then return the recipie
}
}
}
现在问题出在Spring安全上下文中。如果我做这样的事情:
<security:intercept-url pattern="/Recipe/*" access="IS_AUTHENTICATED_FULLY" />
和
<beans:bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="useReferer" value="true" />
</beans:bean>
现在显然在这种情况下,isAuthenticated()
将始终返回true,因为拦截模式将处理它。所以让我删除拦截网址,并尝试这个REST URL CALL:
GET http://bestrecepiesever/Recipe/1
现在1是一个biskett配方,所以任何人都可以得到它并且通话有效。现在我想要一个cookie,所以我试试这个:
GET http://bestrecepiesever/Recipe/420
现在我的Utils类将检查我的会话,看到我没有记录它并抛出异常。这就是我的问题所在......我希望能够为http://bestrecepiesever/Recipe/420添加书签。当我去那里时,我会重新登录我的登录页面,登录后,我会被重新定向回配方。
如何使用Spring Security执行此操作?我放在哪里//做一些事情来进行身份验证会发现我此时未经过身份验证,请将我发送到登录页面,然后再返回配方。
为了使事情变得复杂,我们有SimpleMappingExceptionResolver
来拦截任何和所有异常,并将您重定向到一个很好的页面找不到。是的,我尝试创建自己的,重定向到登录页面,这是有效的,但不会回到referer页面。你可以在这里看到这个问题:Overriding HandlerExceptionResolver not useing Referer
更新
根据@chaoluo这里是utils中的getAuthentication方法:
public static User getUser(IDaoFactory daoFactory) throws Exception {
String authClass = SecurityContextHolder.getContext().getAuthentication().getPrincipal().getClass().getName();
authClass = SecurityContextHolder.getContext().getAuthentication().getAuthorities().getClass().getName();
if (authClass == null) {
authClass = "[ unknown ]";
}
if (SecurityContextHolder.getContext().getAuthentication().getName().toLowerCase().equals("anonymoususer")) {
throw new Exception("not authorized");
}
User theUser = new MyUserDetailsService(daoFactory).loadUserByUsername(SecurityContextHolder.getContext().getAuthentication().getName());
if (theUser == null) {
logger.error("Unable to find user in DB for " + SecurityContextHolder.getContext().getAuthentication().getName());
}
return theUser;
}
但是,这与SpringSecurity中使用的MyAuthenticationProvider完全不同,如下所示:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="myAuthenticationProvider"/>
</security:authentication-provider>
</security:authentication-manager>
验证方法:
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException
{
String username = authentication.getName();
String password = (String) authentication.getCredentials();
//System.out.println(org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable));
IDaoFactory daoFactory = ServiceFactory.getDaoFactory();
try {
QadoUserDetailsService userService = new QadoUserDetailsService(daoFactory);
User user = userService.loadUserByUsername(username);
if (user == null) {
throw new BadCredentialsException(BAD_USER);
}
if (user.getPassword() == null || user.getSalt() == null ||
!PasswordEncryption.hashEquals(user.getSalt(), user.getPassword(), password)) {
throw new BadCredentialsException(BAD_PASSWORD);
}
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(username, password, authorities);
}
finally {
daoFactory.cleanup();
}
}
那么有没有办法通过控制器内的MyAuthenticationProvider进行身份验证?
答案 0 :(得分:0)
你正在使事情复杂化。而不是@PreAuthorize
使用@PostAuthorize
来进行验证。这将使您的控制器保持清洁,让Spring Security处理这些事情。
@RequestMapping(value="/Recipe/{recipeId}", method = RequestMethod.GET)
@PostAuthorize("returnObject.bisket || (returnObject.secretCookieRecipe && isAuthenticated())")
public Recipe getRecipe(@PathVariable("recipeId") int recepieId) {
return database.getRecipeById(recepieId);
}
现在,Spring Security将使用当前用户验证returnObject
。