我正在尝试在spring boot rest服务项目中启用Spring安全性,但是我遇到了一些问题。
我使用此代码配置了
@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private LdapAuthenticationProvider ldapAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapAuthenticationProvider);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
并实现了一个自定义身份验证提供程序,以便登录到LDAP(具有非标准配置,因此我无法使默认的ldap提供程序工作)
@Component
public class LdapAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String email = authentication.getName();
String password = authentication.getCredentials().toString();
LdapConnection ldap = new LdapConnection();
String uid = ldap.getUserUID(email);
if(uid == null || uid == ""){
throw new BadCredentialsException("User " + email + " not found");
}
if(ldap.login(uid, password)){
return new UsernamePasswordAuthenticationToken(uid, null, new ArrayList<>());
}else{
throw new BadCredentialsException("Bad credentials");
}
}
@Override
public boolean supports(Class<?> authentication) {
return true;
//To indicate that this authenticationprovider can handle the auth request. since there's currently only one way of logging in, always return true
}
}
此代码工作正常,在某种意义上,使用基本授权标头调用我的服务,它能够正确登录并返回所调用的服务。当我尝试插入不同的授权/身份验证时,问题就出现了。我不想使用基本身份验证,而是希望从我的react前端的表单中传递凭证,所以我想在POST调用中将它们作为json主体传递。 (然后想法生成一个jwt令牌并将其用于以下通信)。
所以我将configure方法更改为:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
并定义了自定义身份验证过滤器:
public class JWTAuthenticationFilter extends
UsernamePasswordAuthenticationFilter {
@Autowired
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException{
String requestBody;
try{
requestBody = IOUtils.toString(req.getReader());
JsonParser jsonParser = JsonParserFactory.getJsonParser();
Map<String, Object> requestMap = jsonParser.parseMap(requestBody);
return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(requestMap.get("email"), requestMap.get("password"), new ArrayList<>()));
}catch(IOException e){
throw new InternalAuthenticationServiceException("Something goes wrong parsing the request body",e );
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException{
JwtTokenProvider tokenProvider = new JwtTokenProvider();
String token = tokenProvider.generateToken(auth.getPrincipal().toString());
Cookie cookie = new Cookie("jwt",token);
cookie.setHttpOnly(true);
cookie.setSecure(true);
res.addCookie(cookie);
}
}
问题是,无论我做什么,运行时似乎根本不会进入此过滤器。我错过了什么?我想这是一个大而愚蠢的东西,但我无法弄清楚......
UPDATE:问题似乎是UsernamePassWordAuthenticationFilter只能通过表单调用。然后我改变我的代码以扩展AbstractAuthenticationProcessingFilter。
修改后的过滤器:
public class JWTAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
super("/api/secureLogin");
this.authenticationManager = authenticationManager;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException{
String requestBody;
try{
requestBody = IOUtils.toString(req.getReader());
JsonParser jsonParser = JsonParserFactory.getJsonParser();
Map<String, Object> requestMap = jsonParser.parseMap(requestBody);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(requestMap.get("email"), requestMap.get("password"), new ArrayList<>());
return authenticationManager.authenticate(token);
}catch(IOException e){
throw new InternalAuthenticationServiceException("Something goes wrong parsing the request body",e );
}
}
}
和修改后的配置方法:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/secureLogin").permitAll()
.antMatchers(HttpMethod.GET, "/api").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
答案 0 :(得分:0)
我看到您在覆盖configure方法时添加配置,尝试在web.xml中添加过滤器映射。在“网络应用程序”下的类似内容节点:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication47
{
class Program
{
const string INPUT_FILENAME = @"c:\temp\test.txt";
const string OUTPUT_FILENAME = @"c:\temp\test1.txt";
static void Main(string[] args)
{
Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();
using (StreamReader reader = new StreamReader(INPUT_FILENAME))
{
string input = "";
string strKey = "";
while ((input = reader.ReadLine()) != null)
{
string[] inputArray = input.Split(new char[] { ',' });
if (inputArray[0].Contains("*"))
{
int key = int.Parse(inputArray[0].Replace("*", ""));
strKey = "**" + key + "**";
}
else
{
strKey = int.Parse(inputArray[0]).ToString();
}
if (dict.ContainsKey(strKey))
{
dict[strKey].Add(inputArray[1]);
}
else
{
dict.Add(strKey, new List<string>() { inputArray[1] });
}
}
}
using (StreamWriter writer = new StreamWriter(OUTPUT_FILENAME))
{
foreach(KeyValuePair<string,List<string>> row in dict)
{
writer.WriteLine("HI:{0},{1}", row.Key, string.Join(",", row.Value));
}
}
}
}
}
答案 1 :(得分:0)
您希望通过访问路径api/secureLogin
来触发过滤器。默认情况下,UsernamePasswordAuthenticationFilter
仅通过访问/login
来触发。
如果您在扩展JWTAuthenticationFilter
的{{1}}的构造函数中添加以下行,则它应该可以工作:
UsernamePasswordAuthenticationFilter
答案 2 :(得分:0)
您好,@ Mikyjpeg
您可以使用 UsernamePassWordAuthenticationFilter 。
只要从带有POST
网址的/login
方法(而不是您的{{1 }}网址),它将被执行。
构造函数使用仅允许以下url和request方法的请求匹配器:
api/secureLogin
答案 3 :(得分:0)
ADAM 的回答对我有用。那些使用基于注解的配置的人可以添加以下内容
@Override protected Filter[] getServletFilters() {
return new Filter[]{
new JwtAuthenticationFilter()
};
}
在你的 AppInitialiser 中扩展 AbstractAnnotationConfigDispatcherServletInitializer