多年之后,我们终于将 Spring 框架更新到最新版本,其中包括一些我已经能够解决的问题。但这个我似乎无法弄清楚。
我们有一个 LoginController,其中包含不同的映射,例如 LoginRedirect.do、ChangePassword.Do 等。但由于某种原因,当它尝试将 war 文件部署到 tomcat 时,我不断收到此错误消息:
原因:java.lang.IllegalStateException:不明确的映射。无法映射“customUserDetailsService2”方法 com.test.controller.LoginController#loginRedirect(ModelMap) 到 {GET [/login/LoginRedirect.do]}:已经有 'loginController' bean 方法 com.test.controller.LoginController#loginRedirect(ModelMap) 已映射。
我没有其他控制器具有这些映射,但这似乎是由我们的 customUserDetailsService2 bean 引起的。我们的 ApplicationContext-security.xml 有这个:
<beans:bean id="customAuthenticationProvider" class="com.test.security.CustomDaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="customUserDetailsService2"/>
</beans:bean>
<beans:bean id="customUserDetailsService2" class="com.test.controller.LoginController"/>
登录控制器看起来像这样:
package com.test.controller;
import com.test.dao.datasnap.ILoginDAO;
import com.test.dao.datasnap.IPublicMethodsDAO;
import com.test.dao.datasnap.IUtilDAO;
import com.test.exception.DataSnapException;
import com.test.model.BasicResult;
import com.test.model.LoginResponse;
import com.test.model.PDCUser;
import com.test.security.CustomUser;
import com.test.security.CustomUserDetailsService;
import com.test.security.Module;
import com.test.translations.UITranslationCache;
import com.test.util.Util;
import jakarta.servlet.http.HttpServletRequest;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Controller
@Scope("request")
@RequestMapping("login")
public class LoginController implements CustomUserDetailsService {
/*
* Autowired DAO for Login
*/
@Autowired
private ILoginDAO dataDAO;
@Autowired
private IUtilDAO utilDAO;
@Autowired
private IPublicMethodsDAO methodsDAO;
/*
* Gets the current logged in user.
*/
@GetMapping(value="/GetUserName.do")
public String printUser(ModelMap modelMap) {
String name = Util.getUserName(); //get logged in username
modelMap.put("data", name);
return "ajax/json/dataresponse";
}
//from the CustomUserDetailsService interface
public UserDetails loadUserByUsernamePasswordAuthenticationToken(UsernamePasswordAuthenticationToken authentication)
throws UsernameNotFoundException, DataAccessException {
/*
* Use the DAO to get the data we want. This is something we really want to delegate so that we can keep our controller code as small
* as possible.
*/
try {
String username = authentication.getName();
if (((username == null) || "".equals(username))) {
throw new BadCredentialsException(UITranslationCache.GetUITranslation(16877, Util.GetCompanyLanguage(), "Username and/or password are not correct."));
}
LoginResponse returnObject = dataDAO.loginToPDC(authentication);
PDCUser pdcUser = (PDCUser) returnObject.getDataObject();
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);
/*
* based on the settings retrieved from DataSnap, we set the appropriate authorities for the user
*/
if (pdcUser != null && pdcUser.getModules() != null) {
Iterator<Module> itr = pdcUser.getModules().iterator();
while(itr.hasNext()) {
Module module = itr.next();
if (module.getGrantedauthority() != null) {
authList.add(new SimpleGrantedAuthority(module.getGrantedauthority()));
}
}
}
UserDetails user = null;
boolean enabled = true;
user = new CustomUser(
pdcUser.getUsername(),
pdcUser.getManNr(),
pdcUser.getPassword(),
enabled,
authList,
returnObject.getSessionId(),
pdcUser.getModules(),
pdcUser.getLanguageCode(),
pdcUser.getRememberMe(),
pdcUser.getPasswordExpired());
return user;
} catch (DataSnapException e) {
throw new AuthenticationServiceException(e.getMessage());
} catch (AuthenticationException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@ResponseStatus(value=HttpStatus.FORBIDDEN)
@GetMapping(value = "/accessdenied.do")
public ModelAndView accessDenied(ModelMap modelmap) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<Module> modules = Util.getModules();
modelmap.put("modules", modules);
modelmap.put("username", auth.getName());
return new ModelAndView("accessdenied", modelmap); // logical view name -> accessdenied.jsp
}
@ResponseStatus(value=HttpStatus.UNAUTHORIZED)
@GetMapping(value ="/LoginRedirect.do")
public String loginRedirect(ModelMap modelmap) throws JSONException{
JSONObject jsonObject = new JSONObject();
jsonObject.put("success", false);
jsonObject.put("message", UITranslationCache.GetUITranslation(16878, Util.GetCompanyLanguage(), "User not authorized to view this page"));
modelmap.put("data", jsonObject);
return "ajax/json/dataresponse";
}
@GetMapping(value ="/LogoutAjax.do")
public String logoutAjax(ModelMap modelmap) throws JSONException{
JSONObject jsonObject = new JSONObject();
jsonObject.put("success", true);
jsonObject.put("message", UITranslationCache.GetUITranslation(16879, Util.GetCompanyLanguage(), "User was succesfully logged out"));
modelmap.put("data", jsonObject);
return "ajax/json/dataresponse";
}
@GetMapping(value ="/ChangePassword.do")
public String changePassword(@RequestParam(required = true, value = "oldpw") String oldpw,
@RequestParam(required = true, value = "newpw") String newpw,
ModelMap modelMap) throws DataSnapException {
BasicResult result = utilDAO.ChangePassword(oldpw, newpw);
modelMap.put("data", result.getResult());
return "ajax/json/dataresponse";
}
}
在 customUserDetailsService2 进行更改之前,LoginController 本身似乎映射了不同的方法。但这以前可以用,所以我有点迷失为什么现在不起作用了。
你的控制器很奇怪,它混合了职责。您的控制器既是控制器又不是控制器。将
UserDetailsService
移动到它自己的组件中,正如它应该的那样。将控制器设置为请求范围也是没有意义的。
最后,由于
UserDetailsService
和 XML 中的声明,您最终得到 2 个实例(这也是错误告诉您的)。
@Controller
您修改后的 XML 配置
@Service
public class MyCustomUserDetailsService implements CustomUserDetailsService {
@Autowired
private ILoginDAO dataDAO;
//from the CustomUserDetailsService interface
public UserDetails loadUserByUsernamePasswordAuthenticationToken(UsernamePasswordAuthenticationToken authentication)
throws UsernameNotFoundException, DataAccessException {
/*
* Use the DAO to get the data we want. This is something we really want to delegate so that we can keep our controller code as small
* as possible.
*/
try {
String username = authentication.getName();
if (((username == null) || "".equals(username))) {
throw new BadCredentialsException(UITranslationCache.GetUITranslation(16877, Util.GetCompanyLanguage(), "Username and/or password are not correct."));
}
LoginResponse returnObject = dataDAO.loginToPDC(authentication);
PDCUser pdcUser = (PDCUser) returnObject.getDataObject();
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);
/*
* based on the settings retrieved from DataSnap, we set the appropriate authorities for the user
*/
if (pdcUser != null && pdcUser.getModules() != null) {
Iterator<Module> itr = pdcUser.getModules().iterator();
while(itr.hasNext()) {
Module module = itr.next();
if (module.getGrantedauthority() != null) {
authList.add(new SimpleGrantedAuthority(module.getGrantedauthority()));
}
}
}
UserDetails user = null;
boolean enabled = true;
user = new CustomUser(
pdcUser.getUsername(),
pdcUser.getManNr(),
pdcUser.getPassword(),
enabled,
authList,
returnObject.getSessionId(),
pdcUser.getModules(),
pdcUser.getLanguageCode(),
pdcUser.getRememberMe(),
pdcUser.getPasswordExpired());
return user;
} catch (DataSnapException e) {
throw new AuthenticationServiceException(e.getMessage());
} catch (AuthenticationException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
您的瘦身控制器
<beans:bean id="customAuthenticationProvider" class="com.test.security.CustomDaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="customUserDetailsService2"/>
</beans:bean>
<beans:bean id="customUserDetailsService2" class="com.test.controller.MyCustomUserDetailsService"/>