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
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();
}
}
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?
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'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.
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