【发布时间】:2015-02-11 01:08:04
【问题描述】:
我在为我的 web 应用程序设置 AJAX 调用的安全性时遇到问题,该应用程序在后端使用 Spring Security 来验证我的请求。
我的应用中有某些端点,如下所示,我正在通过我的网站和移动应用(Andriod、iOS)访问它们。
我现在已经为所有 /api/* urls 添加了基本身份验证,但这在移动应用访问的情况下很好,但如果我在我的网站的 AJAX 调用中使用相同的身份验证,那么我需要将用户名和密码作为标题请求传递,就像this link 中提到的那样,但我不想向外界透露用户名和密码,因为任何人都可以通过在 JS 文件上查看源代码来看到它们。
现在我想要的是任何 具有 ROLE_API 的用户名和密码都可以访问 /api/* url。但是当我从网站访问这些 URL 时,当我需要使用 AJAX 请求执行任何 CRUD 操作时,如何以安全的方式传递用户名和密码,以至于没有其他 URL 可以访问 /api/* urls来自我的网站。
下面是我的 security-config.xml、AuthenticationManager 和 Controller 的代码。
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:http auto-config="false" use-expressions="true"
access-denied-page="/login" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/login" access="permitAll"/>
<security:intercept-url pattern="/api/**" access="hasRole('ROLE_API')"/>
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')"/>
<security:intercept-url pattern="/user/**" access="hasRole('ROLE_USER')"/>
<security:logout invalidate-session="true" logout-success-url="/login" logout-url="/logout" />
<security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER" />
<security:custom-filter ref="authenticationFilter" position="BASIC_AUTH_FILTER" />
</security:http>
<security:global-method-security secured-annotations="enabled" />
<bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
p:authenticationManager-ref="customAuthenticationManager"
p:authenticationFailureHandler-ref="customAuthenticationFailureHandler"
p:authenticationSuccessHandler-ref="customAuthenticationSuccessHandler" />
<bean id="customAuthenticationManager"
class="com.lumin.mytalk.authentication.CustomAuthenticationManager" />
<bean id="customAuthenticationFailureHandler"
class="com.lumin.mytalk.authentication.AuthenticationFailureUrlHandler" />
<bean id="customAuthenticationSuccessHandler"
class="com.lumin.mytalk.authentication.AuthenticationUrlHandler" />
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"
p:loginFormUrl="/login" />
<security:authentication-manager />
</beans>
CustomAuthenticationManager.java
public class CustomAuthenticationManager implements AuthenticationManager {
protected static Logger logger = Logger
.getLogger(CustomAuthenticationManager.class.getName());
@Autowired
private UserService userService;
private Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
System.out.println("Performing custom authentication");
// Init a database user object
User user = null;
if (auth != null && auth.getName() != null && !auth.getName().equals("")) {
try {
// Retrieve user details from database
String username = auth.getName();
user = userService.getByEmail(username);
} catch (Exception e) {
System.out.println("User does not exists!");
throw new BadCredentialsException("User does not exists!");
}
if(user.getIsSocialUser() != null && user.getIsSocialUser()){
return new UsernamePasswordAuthenticationToken(auth.getName(), null, getAuthorities(user.getRole()));
}else{
String pwd = (String) auth.getCredentials();
if (passwordEncoder.encodePassword(pwd, null).equals(
user.getPassword())) {
System.out.println("Correct Password!");
System.out.println("User details are good and ready to go");
return new UsernamePasswordAuthenticationToken(auth.getName(), auth.getCredentials(), getAuthorities(user.getRole()));
} else {
System.out.println("Incorrect Password");
throw new BadCredentialsException("Incorrect Password !");
}
}
} else {
System.out.println("Username and Password are required");
throw new BadCredentialsException("Username and Password are required !");
}
}
public Collection<GrantedAuthority> getAuthorities(String role) {
// Create a list of grants for this user
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(0);
// Check if this user has admin access
if (role.equals(USERROLE.ROLE_USER.name())) {
System.out.println("Grant ROLE_USER to this user");
authList.add(new SimpleGrantedAuthority(USERROLE.ROLE_USER.name()));
} else if (role.equals(USERROLE.ROLE_ADMIN.name())) {
System.out.println("Grant ROLE_ADMIN to this user");
authList.add(new SimpleGrantedAuthority(USERROLE.ROLE_ADMIN.name()));
}
// Return list of granted authorities
return authList;
}
}
样品架控制器
@RestController
public class CategoryController extends AbstractGenericController{
@Autowired
private CategoryService categoryService;
@ResponseBody
@RequestMapping(value = Path.Url.API + Path.Url.CATEGORY + Path.OperationUrl.CREATE, method = RequestMethod.POST, produces = { "application/json", "application/xml" })
public Object create(@RequestBody Category category) {
Category isExisting = categoryService.getCategoryByName(category.getCategoryName());
if (isExisting == null) {
Long id = categoryService.createWithID(category);
return new Response(null, STATUS.SUCCESS, "Category created successfully. Id :" + id);
}else{
return new Response(null, STATUS.FAILURE, "Category already exist. Please use update category function.");
}
}
}
【问题讨论】:
-
你有什么问题?为什么将用户名和密码存储在 JS 文件中?我希望每个用户都不同。
-
@zeroflagL 我想要的是带有 ROLE_API 的用户名和密码可以访问 /api/* url。但是当我从网站访问这些 URL 时,当我需要使用 AJAX 请求使用相同的 URL 进行任何 CRUD 操作时,我如何将数据传递给 /api/* urls。
-
您的评论和更新不会让事情变得更清楚。假设有一个用户 john。这个用户可以同时拥有
ROLE_USER和ROLE_API,对吗?然后在某些时候必须输入凭据并将这些凭据发送到服务器。显然你知道怎么做,所以我仍然没有看到你的问题。如果您只有一个用户使用ROLE_API,那么您做错了什么。这基本上就像根本没有身份验证一样。
标签: java ajax spring-mvc spring-security