springsecurity6 I didn't authenticate successfully after logging in - java

LoginFilter Implement passing JSON login
/**
* #ClassName: loginFilter
* #Description: 自定义前后端分离
* #author: 沭冘
* #date: 2023/2/12 21:05
*/
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("========================================");
//1.判断是否是 post 方式请求
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//2.判断是否是 json 格式请求类型
if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
//3.从 json 数据中获取用户输入用户名和密码进行认证 {"uname":"xxx","password":"xxx"}
try {
Map<String, String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String username = userInfo.get(getUsernameParameter());
String password = userInfo.get(getPasswordParameter());
System.out.println("用户名: " + username + " 密码: " + password);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
e.printStackTrace();
}
}
return super.attemptAuthentication(request, response);
}
}
There is no problem with customizing the data source
import java.util.HashMap;
import java.util.Map;
/**
* #ClassName: SecurityConfig
* #Description:
* #author: 沭冘
* #date: 2023/2/12 20:47
*/
#Configuration
public class SecurityConfig {
private final MyUserDetailService myUserDetailService;
#Autowired
public SecurityConfig(MyUserDetailService myUserDetailService) {
this.myUserDetailService = myUserDetailService;
}
#Autowired
public void initialize(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(myUserDetailService);
}
#Lazy
#Autowired
public AuthenticationManager authenticationManager;
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Bean
public LoginFilter loginFilter() {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setFilterProcessesUrl("/doLogin");
loginFilter.setUsernameParameter("uname");
loginFilter.setPasswordParameter("passwd");
loginFilter.setAuthenticationManager(authenticationManager);
//认证成功处理
loginFilter.setAuthenticationSuccessHandler((req, resp, authentication) -> {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "登录成功");
result.put("用户信息", authentication.getPrincipal());
resp.setContentType("application/json;charset=UTF-8");
resp.setStatus(HttpStatus.OK.value());
String s = new ObjectMapper().writeValueAsString(result);
resp.getWriter().println(s);
});
//认证失败处理
loginFilter.setAuthenticationFailureHandler((req, resp, ex) -> {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "登录失败: " + ex.getMessage());
resp.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
resp.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
resp.getWriter().println(s);
});
return loginFilter;
}
#Bean
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, resp, ex) -> {
resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
resp.setStatus(HttpStatus.UNAUTHORIZED.value());
resp.getWriter().println("请认证之后再去处理!");
})
.and()
.logout()
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/logout", HttpMethod.DELETE.name()),
new AntPathRequestMatcher("/logout", HttpMethod.GET.name())
))
.logoutSuccessHandler((req, resp, auth) -> {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "注销成功");
result.put("用户信息", auth.getPrincipal());
resp.setContentType("application/json;charset=UTF-8");
resp.setStatus(HttpStatus.OK.value());
String s = new ObjectMapper().writeValueAsString(result);
resp.getWriter().println(s);
})
.and()
.csrf().disable();
httpSecurity.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
return httpSecurity.build();
}
}
Only the login is successful without authentication for other ports
I don't know why I can't get certified after logging in
enter image description here
The login has been successful
but
enter image description here

Related

custom exception is not passing through Advice control in spring boot

I'm trying to customize a thrown exception, but it's not in the advice control.
The TokenExpiredException exception
should be handled in controller advice, but returns a common, unhandled error.
JWTValidarFilter:
public class JWTValidarFilter extends BasicAuthenticationFilter{
private static final String HEADER_ATRIBUTO = "Authorization";
private static final String ATRIBUTO_PREFIXO = "Bearer ";
public JWTValidarFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String atributo = request.getHeader(HEADER_ATRIBUTO);
if(atributo == null) {
chain.doFilter(request, response);
return;
}
if(!atributo.startsWith(ATRIBUTO_PREFIXO)) {
chain.doFilter(request, response);
return;
}
String token = atributo.replace(ATRIBUTO_PREFIXO, "");
UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(token);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthenticationToken(String token) {
try {
String usuario = JWT.require(Algorithm.HMAC512(JWTAutenticarFilter.TOKEN_SENHA))
.build()
.verify(token)
.getSubject();
if(usuario == null) {
return null;
}
return new UsernamePasswordAuthenticationToken(usuario, null, new ArrayList<>());
} catch (TokenExpiredException e) {
throw new TokenExpiredException("Token expirado!");
}
}
}
CustomizeResponseEntityExceptionHandler:
#ControllerAdvice
#RestController
public class CustomizeResponseEntityExceptionHandler extends ResponseEntityExceptionHandler{
#ExceptionHandler(Exception.class)
public final ResponseEntity<ExceptionResponse> handleAllExcepetions(Exception ex, WebRequest request){
ExceptionResponse exceptionResponse =
new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(InvalidJwtAuthenticationException.class)
public final ResponseEntity<ExceptionResponse> invalidJwtAuthenticationException(Exception ex, WebRequest request){
ExceptionResponse exceptionResponse =
new ExceptionResponse(new Date(),
ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(exceptionResponse, HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(TokenExpiredException.class)
public final ResponseEntity<ExceptionResponse> TokenExpiredException(TokenExpiredException ex, WebRequest request){
ExceptionResponse exceptionResponse =
new ExceptionResponse(new Date(),
ex.getMessage(),
request.getDescription(true));
return new ResponseEntity<>(exceptionResponse, HttpStatus.UNAUTHORIZED);
}
#ExceptionHandler(HttpClientErrorException.class)
public ResponseEntity<String> handleException(HttpClientErrorException ex) throws HttpClientErrorException {
System.out.println("*******Exception Occured: *************" + ex);
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(" -----DD------ Exception: " + ex.getLocalizedMessage());
}
}
2021-09-28 10:13:38.729 ERROR 25385 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
com.auth0.jwt.exceptions.TokenExpiredException: Token expirado!
Try building a custom AuthenticationFailureHandler as follows:
public class CustomAuthenticationFailureHandler
implements AuthenticationFailureHandler {
private ObjectMapper objectMapper = new ObjectMapper();
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
Throwable cause = exception.getCause();
ExceptionResponse exceptionResponse = null;
if (cause instanceOf InvalidJwtAuthenticationException) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
exceptionResponse = new ExceptionResponse(new Date(),
cause.getMessage(),
request.getDescription(false));
} else if (cause instanceOf TokenExpiredException) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
exceptionResponse = new ExceptionResponse(new Date(),
ex.getMessage(),
request.getDescription(true));
} else {
// additional logic here
}
response.getOutputStream().println(objectMapper.writeValueAsString(exceptionResponse));
}
}
Then you need to register it in a #Configuration class:
#Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new CustomAuthenticationFailureHandler();
}
You can't catch authentication exceptions using controller advice (at least not as simple :-). Authentication exceptions happen before the whole Spring exception handler intialized. Here ist similar discussion how to workaround related issues:
Spring MVC (or Spring Boot). Custom JSON response for security related exceptions like 401 Unauthorized or 403 Forbidden)
Authentication Exception can not be handled using such #ExceptionHandler, because it just happens before the Spring MVC dispatcher servlet were entered and all the MVC handlers were initialized. So you need to create a Class that inherit interface AuthenticationEntryPoint, override the commence() method and handle the exception there as you need. Here is the example:
#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());
ObjectMapper mapper = new ObjectMapper();
UnauthorizedException exception = new UnauthorizedException("Please provide auth token");
String responseMsg = mapper.writeValueAsString(exception);
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpServletResponse.getWriter().write(responseMsg);
}
}
Add add below lines to your Spring Security Config that extends WebSecurityConfigurerAdapter:
http.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
and it becomes like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true)
public class SecutiryConfig extends WebSecurityConfigurerAdapter {
private final CustomUserDetailsServiceImpl customUserDetailsService;
private final JwtAuthenticationEntryPoint unauthorizedHandler;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
#Autowired
public SecutiryConfig(UserRepository userRepository,
CustomUserDetailsServiceImpl customUserDetailsService,
JwtAuthenticationEntryPoint unauthorizedHandler,
JwtAuthenticationFilter jwtAuthenticationFilter)
{
this.customUserDetailsService = customUserDetailsService;
this.unauthorizedHandler = unauthorizedHandler;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler) // this line
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
}

Authorization doesn't work after replacing HTTP with HTTPS

I created an application with spring-security and jwt. It worked good. But this doesn't work after I replaced HTTP with HTTPS. I have successeful login, but if I use any POST request to a secure page then I get a 403 error. Other requests, such as GET, work.
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
public static final String USER_ROLE = "USER";
public static final String ADMIN_ROLE = "ADMIN";
#Autowired
private DataSource dataSource;
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("https://localhost:8888"));
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.requiresChannel()
.anyRequest()
.requiresSecure();
httpSecurity.cors();
httpSecurity.csrf()
.ignoringAntMatchers("/login")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
httpSecurity.requiresChannel().and().authorizeRequests()
.antMatchers("/recipe/add/**", "/comment/getModerate/**", "/recipe/changeRecipe/**")
.hasAnyRole(USER_ROLE, ADMIN_ROLE)
.antMatchers("/**")
.permitAll()
.and()
.addFilterBefore(new JWTLoginFilter("/login", authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
httpSecurity
.logout()
.logoutSuccessHandler(logoutHandler())
.deleteCookies("JSESSIONID", "COOKIE-BEARER")
.invalidateHttpSession(true)
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.passwordEncoder(passwordEncoder())
.usersByUsernameQuery("select username,password,enabled from users where username = ?")
.authoritiesByUsernameQuery("select rs.username, r.roles from users rs inner join user_role r on rs.id = r.role_id where rs.username = ?");
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
return encoder;
}
#Bean
public CustomLogoutSuccessHandler logoutHandler() {
return new CustomLogoutSuccessHandler();
}
}
JWTAuthenticationFilter.java
public class JWTAuthenticationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException,
IOException {
try {
Authentication authentication = TokenAuthenticationHelper.getAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
} catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException |
SignatureException | IllegalArgumentException e) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token expired");
}
}
}
JWTLoginFilter.java
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {
public JWTLoginFilter(String url, AuthenticationManager authManager) {
super(new AntPathRequestMatcher(url));
setAuthenticationManager(authManager);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws IOException {
AccountCredentials creds = new ObjectMapper().readValue(req.getInputStream(), AccountCredentials.class);
return getAuthenticationManager().authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
creds.getAuthorities()
)
);
}
#Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) {
TokenAuthenticationHelper.addAuthentication(res, auth);
}
static class AccountCredentials {
private String username;
private String password;
private Collection<GrantedAuthority> authorities;
String getUsername() {
return username;
}
void setUsername(String username) {
this.username = username;
}
String getPassword() {
return password;
}
void setPassword(String password) {
this.password = password;
}
Collection<GrantedAuthority> getAuthorities() {
return authorities;
}
void setAuthorities(Collection<GrantedAuthority> authorities) {
this.authorities = authorities;
}
}
}
TokenAuthenticationHelper.java
class TokenAuthenticationHelper {
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 3; // 3 hours
private static final String SECRET = "ThisIsASecret";
private static final String COOKIE_BEARER = "COOKIE-BEARER";
private TokenAuthenticationHelper() {
throw new IllegalStateException("Utility class");
}
static void addAuthentication(HttpServletResponse res, Authentication auth) {
String authorities = auth.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
String jwt = Jwts.builder()
.setSubject(auth.getName())
.claim("authorities", authorities)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
Cookie cookie = new Cookie(COOKIE_BEARER, jwt);
cookie.setHttpOnly(true);
cookie.setPath("/");
res.addCookie(cookie);
}
static Authentication getAuthentication(HttpServletRequest request) {
System.out.println(request);
Cookie cookie = WebUtils.getCookie(request, COOKIE_BEARER);
String token = cookie != null ? cookie.getValue() : null;
if (token != null) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get("authorities").toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
String userName = claims.getSubject();
return userName != null ? new UsernamePasswordAuthenticationToken(userName, null, authorities) : null;
}
return null;
}
}
ServerConfig.java
#Configuration
public class ServerConfig {
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("ROLE_ADMIN");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(getHttpConnector());
return tomcat;
}
private Connector getHttpConnector() {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8081);
return connector;
}
}
application.propeties
server.port=8081
#server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:keystore.p12
server.ssl.key-store-password=123456
server.ssl.key-alias=back
I think the problem is with a csrf token. I can not csrf().disable(). Can you help me undertand what is wrong?

Authentication Manager in JWT Custom Filter

I'm writing a program which uses JWT authentication with Spring Security. I've implemented custom Authorization and Authentication filters. Also, i need to persist my tokens, which formed by these filters. For this purposes, I've created Token DAO service, which autowired to filters, and marked My filters with #Component annotation to autowire this service. But I'm not able to autowire authentication manager correctly.
I`ve tried to expose in Security Configuration class Authentication Manager bean, but get no result.
This is my Security Configuration Class:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
#ComponentScan(value = "ua.edu.viti.medex")
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
UsersDetailsService usersDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(usersDetailsService).passwordEncoder(encoder());
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.authorizeRequests()
.antMatchers("/signup").hasRole("ADMIN")
.antMatchers("/login").permitAll()
.antMatchers("/signout").permitAll()
.antMatchers("/**").authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManagerBean()))
.addFilter(new JwtAuthorizationFilter(authenticationManagerBean()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
This is my JwtAuthenticationFilter
#Component
#SuppressWarnings({"WeakerAccess", "SpringJavaAutowiredMembersInspection"})
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Autowired
TokenServiceImpl tokenService;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
setAuthenticationManager(authenticationManager);
setFilterProcessesUrl(SecurityConstants.AUTH_LOGIN_URL);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
String body = "";
if ("POST".equalsIgnoreCase(request.getMethod()))
{
try {
body = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
} catch (IOException e) {
e.printStackTrace();
}
}
GsonJsonParser jsonParser = new GsonJsonParser();
Map<String, Object> data = jsonParser.parseMap(body);
Map<String, Object> credentials = jsonParser.parseMap((String) data.get("data"));
String username = (String) (credentials != null ? credentials.get("username") : null);
String password = (String) (credentials != null ? credentials.get("password") : null);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return authenticationManager.authenticate(authenticationToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain, Authentication authentication) throws IOException {
User user = ((User) authentication.getPrincipal());
Tokens token = null;
try{
token = tokenService.getTokenFromEmail(user.getUsername());
} catch (NotFoundException e) {
List<String> roles = user.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
byte[] signingKey = SecurityConstants.JWT_SECRET.getBytes();
String newToken = Jwts.builder()
.signWith(Keys.hmacShaKeyFor(signingKey), SignatureAlgorithm.HS512)
.setHeaderParam("typ", SecurityConstants.TOKEN_TYPE)
.setIssuer(SecurityConstants.TOKEN_ISSUER)
.setAudience(SecurityConstants.TOKEN_AUDIENCE)
.setSubject(user.getUsername())
.setExpiration(new Date(System.currentTimeMillis() + 7200000))
.claim("role", roles)
.compact();
String completeToken = SecurityConstants.TOKEN_PREFIX + newToken;
tokenService.addTokenData(completeToken);
PrintWriter out = response.getWriter();
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
out.print(completeToken);
out.flush();
response.addHeader(SecurityConstants.TOKEN_HEADER, SecurityConstants.TOKEN_PREFIX + newToken);
return;
}
if(token.isValid() && (System.currentTimeMillis() - token.getExpiration().getTime() > 900000)){
String completeToken = token.getToken();
PrintWriter out = response.getWriter();
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
out.print(completeToken);
out.flush();
response.addHeader(SecurityConstants.TOKEN_HEADER, completeToken);
return;
}else {
String completeToken = null;
try {
completeToken = tokenService.refreshToken(token).getToken();
} catch (NotFoundException e) {
e.printStackTrace();
}
PrintWriter out = response.getWriter();
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
out.print(completeToken);
out.flush();
response.addHeader(SecurityConstants.TOKEN_HEADER, completeToken);
}
}
}
This error which I`ve get when trying to build project.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jwtAuthenticationFilter' defined in file [D:\Master\MedEx\AuthModule\target\classes\ua\edu\viti\medex\auth\config\secutiry\JwtAuthenticationFilter.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: authenticationManager must be specified
Problem solved by addind DAO class as constructor parameter.
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager(), tokenDAO))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), tokenDAO))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
#Autowired
TokenDAOImpl tokenDAO;
Then no neseccity to make filter as Spring Component.

I can not retrieve the current custom user object from Spring Security Context

I'm using LDAP authentication provider (active directory) + JWT authorization filter.
I have my custom user object implementing UserDetails, also my user service extends UserDetailsService. But when I do:
Usuario principal = (Usuario) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Principal is just a string (the username), not my user object.
This is my configuration:
SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final LdapProperties ldapProperties;
private final LdapUserMapper ldapUserMapper;
private final UserService userService;
public SecurityConfig(LdapProperties ldapProperties, LdapUserMapper ldapUserMapper, UserService userService) {
this.ldapProperties = ldapProperties;
this.ldapUserMapper = ldapUserMapper;
this.userService = userService;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// Entry points
http.authorizeRequests()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers(HttpMethod.GET, "/v2/api-docs",
"/configuration/ui",
"/swagger-resources",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**",
"/swagger-resources/**",
"/swagger-ui.html").permitAll()
//TODO review
.anyRequest().authenticated();
// JwtWebSecurityConfigurer... TODO ?
// Filters
http.addFilter(new AuthenticationFilter(authenticationManager())); // ldap
http.addFilter(new AuthorizationFilter(authenticationManager())); // jwt
http.cors();
http.csrf().disable();
// No session will be created or used by spring security
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Need to provide Authorization header
http.httpBasic();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapAuthenticationProvider());
auth.userDetailsService(userService);
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues();
corsConfiguration.setAllowedMethods(Arrays.asList(CorsConfiguration.ALL));
//TODO configure properly
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
#Bean
public AbstractLdapAuthenticationProvider ldapAuthenticationProvider() {
String urls = "";
for (String url : ldapProperties.getUrls()) {
urls += url + " ";
}
urls = urls.trim();
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
ldapProperties.getBaseEnvironment().get("domain"),
urls,
ldapProperties.getBase()
);
provider.setUserDetailsContextMapper(ldapUserMapper);
provider.setConvertSubErrorCodesToExceptions(true);
// comment to connect as anonymous
provider.authenticate(
new UsernamePasswordAuthenticationToken(ldapProperties.getUsername(), ldapProperties.getPassword())
);
return provider;
}
}
LdapUserMapper:
#Component
public class LdapUserMapper implements UserDetailsContextMapper {
private final UserService userService;
#Autowired
public LdapUserMapper(UserService userService) {
this.userService = userService;
}
#Override
public UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<? extends GrantedAuthority> authorities) {
Usuario result = (Usuario) userService.loadUserByUsername(username);
//TODO compare roles ? set bloqueado ? ...
return result;
}
#Override
public void mapUserToContext(UserDetails userDetails, DirContextAdapter dirContextAdapter) {
}
}
It was my authorization filter.
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String token = req.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.replace("Bearer ", "");
Authentication authentication = getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(final String token) {
try {
DecodedJWT decodedToken = JWT.require(Algorithm.HMAC512(jwtSecret)).build().verify(token);
String username = decodedToken.getSubject();
return new UsernamePasswordAuthenticationToken(username , null, new ArrayList<>());
} catch (JWTVerificationException ex) {
log.error(ex.getMessage());
}
return null;
}
I wasn't aware that I only was passing the username as the Principal object. So I changed it to this:
Usuario usuario = (Usuario) userService.loadUserByUsername(decodedToken.getSubject());
I also removed
auth.userDetailsService(userService);
From AuthenticationManagerBuilder config. That was wrong. My auth provider is LDAP, not the DB.

Spring boot Security - ldap Authentication with Database roles

Im working on a project with spring boot and spring security, I want to authenticate the user against Active Directory over LDAP Protocol and retrieving authorization of this user from User Table (DB).
how can i tell spring security to get aithorization from db instead of authorities from AD and how can i ensure that a given user in AD has his own record in DB?
My SecurityConfiguration Class :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final UserDetailsService userDetailsService;
private final CorsFilter corsFilter;
public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,
CorsFilter corsFilter) {
this.authenticationManagerBuilder = authenticationManagerBuilder;
this.userDetailsService = userDetailsService; this.corsFilter = corsFilter;
}
#PostConstruct
public void init() {
try {
authenticationManagerBuilder
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource(contextSource())
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
} catch (Exception e) {
throw new BeanInitializationException("Security configuration failed", e);
}
}
#Bean
public AjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandler() {
return new AjaxAuthenticationSuccessHandler();
}
#Bean
public AjaxAuthenticationFailureHandler ajaxAuthenticationFailureHandler() {
return new AjaxAuthenticationFailureHandler();
}
#Bean
public AjaxLogoutSuccessHandler ajaxLogoutSuccessHandler() {
return new AjaxLogoutSuccessHandler();
}
#Bean
public Http401UnauthorizedEntryPoint http401UnauthorizedEntryPoint() {
return new Http401UnauthorizedEntryPoint();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/bower_components/**")
.antMatchers("/content/**"); }
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(http401UnauthorizedEntryPoint())
.and()
.formLogin()
.loginProcessingUrl("/api/authentication")
.successHandler(ajaxAuthenticationSuccessHandler())
.failureHandler(ajaxAuthenticationFailureHandler())
.usernameParameter("usr")
.passwordParameter("pwd")
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler())
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/authenticate").permitAll()
.antMatchers("/api/**").authenticated();
}
#Bean
public DefaultSpringSecurityContextSource contextSource() {
return new DefaultSpringSecurityContextSource(Arrays.asList("ldap://localhost:8389/"), "dc=springframework,dc=org");
}
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
return new SecurityEvaluationContextExtension();
}
My UserDetailsService is :
#Component("userDetailsService")
public class DomainUserDetailsService implements UserDetailsService {
private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
private final UserRepository userRepository;
public DomainUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
#Transactional
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
Optional<User> userFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
return userFromDatabase.map(user -> {
if (!user.getActivated()) {
throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
}
List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(lowercaseLogin,
user.getPassword(),
grantedAuthorities);
}).orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " +
"database"));
}
}
SecurityUtils:
public static String getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
String userName = null;
if (authentication != null) {
if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
userName = springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
userName = (String) authentication.getPrincipal();
}
}
return userName;
}
I'm really confused and I don't know how to make things works
Thank you so much for the help

Categories