我已经使用Spring Security实现了登录和密码的注册和身份验证。我是用这个教程做的。但现在我想使用Facebook Id提供注册和身份验证。我将在移动应用程序上获取Facebook用户的访问令牌,然后我将此访问令牌发送到my Spring服务器(因此OAuth不适用),使用Facebook API验证它,并获取Facebook用户Id。请注意,我的数据库中已经有表用户
,登录
,password
,fb_id
列,所以注册有两种变体:要么用户有登录名和密码,要么用户只有Facebook帐户,我将使用他的Facebook id。
最后,我只需要验证这个用户并将JWT令牌返回给他。
已经存在的代码。
SecurityConfig:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability")
.permitAll()
.antMatchers(HttpMethod.GET, "/api/polls/**", "/api/users/**")
.permitAll()
.anyRequest()
.authenticated();
// Add our custom JWT security filter
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
当前用户:
@Target({ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface CurrentUser {
}
客户服务:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
UserRepository userRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// Let people login with either username or email
User user = userRepository.findByPhone(username)
.orElseThrow(() ->
new UsernameNotFoundException("User not found with username or email : " + username)
);
return user;
}
// This method is used by JWTAuthenticationFilter
@Transactional
public UserDetails loadUserById(Long id) {
User user = userRepository.findById(id).orElseThrow(
() -> new UsernameNotFoundException("User not found with id : " + id)
);
return user;
}
}
JwtAuthenticationEntryPoint:
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Sorry, You're not authorized to access this resource.");
}
}
Jwt身份验证过滤器:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenProvider tokenProvider;
@Autowired
private CustomUserDetailsService customUserDetailsService;
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
logger.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
JwtTokenProvider:
@Component
public class JwtTokenProvider {
private static final Logger logger = LoggerFactory.getLogger(JwtTokenProvider.class);
@Value("${app.jwtSecret}")
private String jwtSecret;
@Value("${app.jwtExpirationInMs}")
private int jwtExpirationInMs;
public String generateToken(Authentication authentication) {
User user = (User) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(Long.toString(user.getId()))
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public Long getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
logger.error("Invalid JWT signature");
} catch (MalformedJwtException ex) {
logger.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
logger.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
logger.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
logger.error("JWT claims string is empty.");
}
return false;
}
}
授权控制器:
//...
@PostMapping("/authorization")
public AuthorizationResponse AuthauthenticateUser(@Valid @RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getPhone(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
User user = (User)authentication.getPrincipal();
AuthorizationResponse authorizationResponse = new AuthorizationResponse(user.getId(), jwt);
return authorizationResponse;
}
//...
那么,如何使用Spring Security实现这一目标呢?
您可以创建一个API
id_令牌
下面是一个示例代码-
@PostMapping("/signin/facebook")
public ResponseEntity<?> signInWithFacebook(@Valid @RequestBody FBLoginRequest loginRequest) {
// Call facebook's API to validate id_token and get user's details
// Register new user (Note: Also add code to check if a user already exists with the given facebookId )
User user = new User(fbName, fbID,
emailId, null);
Role userRole = roleRepository.findByName(RoleName.ROLE_USER)
.orElseThrow(() -> new AppException("User Role not set."));
user.setRoles(Collections.singleton(userRole));
User result = userRepository.save(user);
// Authenticate User
UserPrincipal userPrincipal = UserPrincipal.create(result);
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(userPrincipal, null, userPrincipal.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
// Generate and return token
String jwt = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
我还没有添加调用facebook应用编程接口的代码。但是实现起来应该是微不足道的。
我正在做一个项目(没有生产级别,只是为了提高我的技能),我正在使用JWT来处理身份验证。从我所读到的内容来看,仅使用JWT作为访问令牌是非常不安全的,因此我们需要刷新令牌。因此,在登录时,服务器返回一个访问令牌和一个刷新令牌(我将存储在httpOnly cookie中)。访问令牌在短时间内到期,但刷新令牌在到期时用于获取新令牌。 我的问题是,我们何时使用刷新令牌来获取新的访问令牌?是当用户想要获得
我试图弄清楚我应该如何坚持身份验证。 假设用户使用电子邮件和密码成功进行身份验证。然后服务器生成并返回两个令牌: accesstoken(jwt过期15分钟)将存储在浏览器存储中 refreshtoken(jwt过期7天)作为安全cookie 当将访问令牌存储在本地存储(或会话存储)中时,React 应用程序将简单地检查它是否存在于存储中并继续渲染私有路由。因此,这意味着如果用户有一个无效/被盗的
我有一个Spring启动的应用程序,用户可以通过登录和密码或数字证书进行身份验证。我试图用用户和密码验证,但当我试图用证书验证时,chrome不显示窗口来选择我想在验证中使用的证书(我有几个证书,服务器是安全的,等等...) 有什么想法吗?我附上我的WebSecurity配置适配器的代码,以防我出错 配置属性包括: 服务器ssl。密钥存储=存储/密钥存储。jks服务器。ssl。密钥存储密码=更改I
我只想验证从javascript获得的用户访问令牌。 我正在使用SpringRest服务。 我看到了这一点:https://developers.google.com/identity/sign-in/web/server-side-flow然而,我正试图找到facebook和谷歌的确切代码。 有人有任何代码片段来分享或指导任何github吗?包括依赖关系,因为它们也会产生一些冲突。 如果有人从不
在我的Jitsi Meet Prodody配置文件中:~/。jitsi meet cfg/prosody/config/conf.d 我有以下配置: 有了它,我可以通过令牌进行身份验证。 但是,如果我不指定任何令牌,例如: https://jitsi.mydummyserver.com/test 然后我得到以下提示询问用户和密码: 有没有办法只允许令牌身份验证并完全摆脱那个提示? 谢谢
我正在使用带有承载策略的Passport AAD项目来保护我的endpoint。当我在登录时收到带有OIDC策略的令牌后,我似乎无法获得承载策略来验证访问令牌的签名。我得到: 我没有问题验证id_token,但如果id_token不能用AAD刷新,我更希望不要在我们的客户端应用程序中使用它。此外,当使用jwt.io测试发布的公钥的验证时,我看到了同样的问题(可以验证id_token,但不能验证ac