用户登录工作良好,但我想添加一个客户模块的项目。我知道我需要编写一个自定义UserDetails类来获取客户用户名,但我想问是否需要为客户登录验证编写另一个自定义JWT过滤器。目前这是过滤器类,我有用户登录。我已经向Customer实体添加了一个用户名和密码字段。
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserAccountService myUserDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
String authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.joining());
System.out.println("Authorities granted : " + authorities);
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
else {
System.out.println("Not Valid Token");
}
}
chain.doFilter(request, response);
}
}
可以看到,Filter正在使用自定义UserDetails来验证用户名。如何将Customer userdetails服务添加到过滤器中?这是我的第一个多重登录项目,请对我宽容。
在记录时区分用户和客户。相应地,调用不同的服务来获取用户详细信息。更多信息可以在这里找到。Spring Security用户对客户和员工的身份验证
-
如何将Customer userdetails服务添加到过滤器中?像注射
UserAccountService
一样注射。如果你这样做,你使用1个过滤器(当然,这个过滤器是在1个SecurityFilterChain
),你基本上可以实现你的过滤器:尝试通过myUserDetailsService
验证你的用户,如果它不成功,继续使用myCustomerDetailsService
。 -
对于多个登录项目。第二种方法是使用2
SecurityFilterChain
。例如,UserJwtFilter用于1个SecurityFilterChain
, CustomJwtFilter用于1个SecurityFilterChain
。人们通常对不同的登录机制这样做Basic, OAuth2, SAML2。例句:
- 基本身份验证:
@Configuration
@Order(2)
public class BasicAuthenticationFilterChain extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/login", "/logout")
.and()
- OAuth2身份认证:
@Configuration
@Order(3)
public class OAuth2AuthenticationFilterChain extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/oauth")
.and()
在这种情况下,当一个带有"/login"它将被定向到BasicAuthenticationFilterChain
,并带有"/oauth"我会去OAuth2AuthenticationFilterChain
。关于Order
:越低优先级越高,一旦请求被SecurityFilterChain
处理,它就不会转到另一个SecurityFilterChain
。你可以用这种方式实现你的项目。
结论:有很多方法可以实现你的spring安全的想法,这取决于你的选择。
在我看来你已经这么做了。
@Autowired
private UserAccountService myUserDetailsService;
但是我建议使用构造函数而不是@Autowired。Spring将以同样的方式填充构造函数参数。当您也使用lombok库时,这可能非常小。使用构造函数还可以使模拟测试更容易一些。
按注释中讨论的更新:
@Log //another lombok thing
@RequiredArgsConstructor
@Component
public class JwtRequestFilter extends Filter{
private final JwtTokenUtil jwtTokenUtil;
private final UserAccountService myUserDetailsService;
private final CustomerAccountService myCustomerDetailsService;
private static final String AUTH_HEADER = "authorization";
@Override
protected void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
String tokenHeader = ((HttpServletRequest) request).getHeader(AUTH_HEADER);
if(hasValue(tokenHeader) && tokenHeader.toLowerCase().startsWith("bearer ")){
jwtToken = requestTokenHeader.substring(7);
String username;
String jwtToken;
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
if (uSecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = myUserDetailsService.loadUserByUsername(username);
if(isNull(userDetails)){
userDetails = myCustomerDetailsService.loadCustomerByUsername(username);
}
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
var token = createSecurityToken(userDetails);
SecurityContextHolder.getContext().setAuthentication(token);
} else {
throw new RuntimeException("Not a Valid Token.");
}
} else {
log.info("Authorization already present");
}
} catch (IllegalArgumentException e) {
throw new("Unable to get JWT Token",e);
} catch (ExpiredJwtException e) {
throw new("JWT Token has expired",e);
}
} else {
throw new RuntimeException("No valid authorization header found.");
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken createSecurityToken(UserDetails userDetails){
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
log.info("Authorities granted : {}", userDetails.getAuthorities());
token.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return token;
}
}