Unauthorization in SpringBoot - java

Can someone tell me why after loggedin to system, in Postman response i get 401 - unauthorization instead of my token?
CorsConfig.java
#Configuration
public class CorsConfig {
#Bean
public CorsFilter corsFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// config.addAllowedOrigin("*");
// config.addAllowedHeader("*");
// config.addAllowedMethod("*");
source.registerCorsConfiguration("/api/**", config);
return new CorsFilter(source);
}
}
SecurityConfig.java
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception{
authenticationManagerBuilder
.userDetailsService(securityUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#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/**", "/api/user/register/**", "/api/user/namecheck/**", "/api/password/**")
// .permitAll()
// .antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability")
// .permitAll()
//.antMatchers(HttpMethod.GET, "/api/polls/**", "/api/users/**")
// .permitAll()
.anyRequest()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}
}
AuthController.java
#RestController
#RequestMapping("/api/auth")
public class AuthController {
#Autowired
AuthenticationManager authenticationManager;
#Autowired
JwtTokenProvider tokenProvider;
#PostMapping
public ResponseEntity<?> authenticateUser(#Valid #RequestBody AuthRequest request){
Object principal;
Object credentials;
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getEmail(),
request.getPassword()
));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = tokenProvider.generateToken(authentication);
return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
}
AuthenticatedUser.java, my model
#Data
#Accessors(chain = true)
#EqualsAndHashCode
public class AuthenticatedUser implements UserDetails {
private UUID id;
private String firstName;
private String lastName;
private String token;
#JsonIgnore
private String email;
#JsonIgnore
private String password;
public AuthenticatedUser(){}
public static AuthenticatedUser mapFromEntity(UserEntity userEntity){
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(userEntity.getRole().name()));
return new AuthenticatedUser()
.setId(userEntity.getId())
.setEmail(userEntity.getEmail())
.setPassword(userEntity.getPassword())
.setFirstName(userEntity.getFirstName())
.setLastName(userEntity.getLastName())
.setAuthorities(authorities);
}
private Collection<? extends GrantedAuthority> authorities;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
TokenProvider.java
#Component
public class JwtTokenProvider {
#Value("${jwt.salt}")
private String jwtSalt;
#Value("${jwt.expiry}")
private int jwtExpiry;
public String generateToken(Authentication authentication) {
AuthenticatedUser userEntity = (AuthenticatedUser) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpiry);
return Jwts.builder()
.setSubject(userEntity.getId().toString())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSalt)
.compact();
}
public UUID getUserIdFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSalt)
.parseClaimsJws(token)
.getBody();
return UUID.fromString(claims.getSubject());
}
public boolean validateToken(String authToken) {
///
JwtauthenticationFilter.java
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private SecurityUserDetailsService securityUserDetailsService;
#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)) {
UUID userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = securityUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, 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);
}
return null;
}

You should /api/auth route to the SecurityConfig
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/api/auth",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
http.headers().cacheControl();
}

Because I can't comment, I'm writing it here.
Make sure like #Pankratiew Alexandr said to add /api/auth endpoint with permitAll().
Then my question is if you have any user already in your memory or database. I mean that when you call method authenticateUser in your controller you use authenticationManager.authenticate(..., so loadByUsername() is called and it may don't load any user with provided e-mail and password.

Related

endpoint for authentication with spring security

I wanna create custom endpoint for login.
#PostMapping("/login")
public ResponseEntity<?> login(#RequestBody LoginUserRequest userRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(userRequest.getUsername(), userRequest.getPassword()));
if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authentication);
return new ResponseEntity<>(null, null, HttpStatus.OK);
}
return new ResponseEntity<>(null, null, HttpStatus.UNAUTHORIZED);
}
It works fine when password and username are correct but returns 200 and login form instead 401 for incorrect data.
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
public SecurityConfiguration(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET).hasAuthority(UserRole.USER.name())
.antMatchers(HttpMethod.POST, "/users").permitAll()
.antMatchers(HttpMethod.POST, "/users/login").permitAll()
.antMatchers(HttpMethod.POST).hasAuthority(UserRole.USER.name())
.and()
.formLogin()
.permitAll()
.and()
.logout().invalidateHttpSession(true)
.clearAuthentication(true).permitAll()
.and()
.csrf().disable();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Try something like that:
Don't forget to Autowire AuthenticationManager and other services!
#RequestMapping(value = "/auth", method = RequestMethod.POST)
public ResponseEntity<?> getAuthenticationToken(
#RequestBody YourRequestDTO yourRequestDTO
) {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getLogin(),
authenticationRequest.getPassword()
)
);
} catch (BadCredentialsException e) {
ErrorResponse errors = new ErrorResponse();
errors.addError("credentials", "Wrong password or username!");
return ResponseEntity.status(YourStatus).body(errors);
}

Spring security mutiple login process doesn't work it supposed to do

I'm suffering for multiple login processing.
I've searched a lot about it and none of answers out there are working.
I wrote two login processing one for admin and the other one for normal user.
And also wrote each success and failure handlers but the handlers are always working with the last one in #order(2) configuration even though I request to /admin.
My problems are:
I can login but the success handler is always triggered as the last one in #order(2).
The failure handler is triggered but as the last one as well regardless what URL I request and it throws 404 error (I can see it goes on proper controller during debugging). Maybe tiles doesn't work on failure process?
Here is my security config:
#Configuration
#EnableWebSecurity
#AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private UsersServiceImpl usersService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/css/**",
"/js/**",
"/img/**",
"/font/**",
"/html/**",
"/jusoPopup",
"favicon.ico"
);
}
#Configuration
#Order(1)
#NoArgsConstructor
public static class AdminConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.defaultSuccessUrl("/admin")
.failureHandler(adminFailureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.and()
.csrf().disable();
}
#Bean
public AuthenticationSuccessHandler adminSuccessHandler() {
return new CustomLoginSuccessHandler("/admin");
}
#Bean
public AuthenticationFailureHandler adminFailureHandler() {
return new CustomLoginFailureHandler("/admin/login?error=true");
}
}
#Configuration
#Order(2)
#NoArgsConstructor
public static class NormalConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/Ticketing/**", "/**/write").hasRole("MEMBER")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.successHandler(successHandler())
.failureHandler(failureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.and()
.headers()
.frameOptions().sameOrigin()
.and()
.csrf().disable();
}
#Bean
public AuthenticationSuccessHandler successHandler() {
return new CustomLoginSuccessHandler("/");
}
#Bean
public AuthenticationFailureHandler failureHandler() {
return new CustomLoginFailureHandler("/login?error=true");
}
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(usersService).passwordEncoder(passwordEncoder());
}
}
class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
public CustomLoginSuccessHandler(String defaultTargetUrl) {
setDefaultTargetUrl(defaultTargetUrl);
}
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication
) throws ServletException, IOException {
HttpSession session = request.getSession();
if (session != null) {
String redirectUrl = (String) session.getAttribute("prevPage");
if (redirectUrl != null) {
session.removeAttribute("prevPage");
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
#Getter
#Setter
#AllArgsConstructor
class CustomLoginFailureHandler implements AuthenticationFailureHandler {
private String defaultFailureUrl;
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception
) throws IOException, ServletException {
String errorMessage = "some error message";
request.setAttribute("errorMessage", errorMessage);
request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
}
}
It's hard to find proper working well multiple login processing resource out there if you guys know the good resource about it please let me know.
I hope it's my code error so that I don't have to change the application structure.
I read some Baeldung guide and spring security documents Here is my edited configuration :
#EnableWebSecurity
public class SecurityConfig{
#NoArgsConstructor
#Configuration
#Order(1)
public static class AdminConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.authorizeRequests().anyRequest().hasRole("ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.defaultSuccessUrl("/admin")
.failureHandler(adminFailureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.and()
.csrf().disable();
}
#Bean
public AuthenticationEntryPoint authenticationEntryPoint(){
BasicAuthenticationEntryPoint entryPoint =
new BasicAuthenticationEntryPoint();
entryPoint.setRealmName("admin realm");
return entryPoint;
}
#Bean
public AuthenticationFailureHandler adminFailureHandler() {
return new CustomLoginFailureHandler("/admin/login");
}
}
#AllArgsConstructor
#Configuration
public static class NormalConfigurationAdapter extends WebSecurityConfigurerAdapter {
private UsersServiceImpl usersService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(
"/css/**",
"/js/**",
"/img/**",
"/font/**",
"/html/**",
"/jusoPopup",
"favicon.ico"
);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/Ticketing/**", "**/write")
.hasRole("MEMBER")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.successHandler(successHandler())
.failureHandler(failureHandler())
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.and()
.headers().frameOptions().sameOrigin()
.and()
.csrf().disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(usersService).passwordEncoder(passwordEncoder());
}
#Bean
public AuthenticationSuccessHandler successHandler() {
return new CustomLoginSuccessHandler("/");
}
#Bean
public AuthenticationFailureHandler failureHandler() {
return new CustomLoginFailureHandler("/login");
}
}
}
class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
public CustomLoginSuccessHandler(String defaultTargetUrl) {
setDefaultTargetUrl(defaultTargetUrl);
}
#Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication
) throws ServletException, IOException {
HttpSession session = request.getSession();
if (session != null) {
String redirectUrl = (String) session.getAttribute("prevPage");
if (redirectUrl != null) {
session.removeAttribute("prevPage");
getRedirectStrategy().sendRedirect(request, response, redirectUrl);
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
} else {
super.onAuthenticationSuccess(request, response, authentication);
}
}
}
#Getter
#Setter
#AllArgsConstructor
class CustomLoginFailureHandler implements AuthenticationFailureHandler {
private String defaultFailureUrl;
#Override
public void onAuthenticationFailure(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception
) throws IOException, ServletException {
String errorMessage = "Error";
request.setAttribute("errorMessage", errorMessage);
request.getRequestDispatcher(defaultFailureUrl).forward(request, response);
}
}
It looks more organized than before i guess but still has the same problem... also read about AuthenticationEntryPoint but not sure it's fit in my case

Create JWT Token within UsernamePasswordAuthenticationFilter or Controller?

I try to generate a JWT Token with Spring Security but I don't how to do it correctly (with the best practice).
Do I should "intercept" the authenticate method within UsernamePasswordAuthenticationFilter somewhere and generate token inside it ?
Or it is better to use AuthenticationManager autowired in the controller '/login' ?
I'm afraid to authenticate the user twice if I use the controller mechanism.
I used this tutorial : tutorial Jwt Token
Here is my code :
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
// #Autowired
// private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenFilter jwtTokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/css/**", "/login/**", "/register/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
//.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
.formLogin()
.usernameParameter("email")
//.loginPage("http://localhost:4200/login").failureUrl("/login-error")
.and()
.logout()
.permitAll();
http
.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public CustomDaoAuthenticationProvider authenticationProvider() {
CustomDaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
authenticationProvider.setUserDetailsService(userService);
return authenticationProvider;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebConfig() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:4200")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
"Access-Control-Request-Headers", "Authorization", "Cache-Control",
"Access-Control-Allow-Origin")
.exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
.allowCredentials(true).maxAge(3600);
}
};
}
}
Token Filter
public class JwtTokenFilter extends GenericFilterBean {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(req, res);
}
}
Token Provider
#Component
public class JwtTokenProvider {
#Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";
#Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h
#Autowired
private UserDetailsService userDetailsService;
#PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
public String createToken(String username, List<String> roles) {
Claims claims = Jwts.claims().setSubject(username);
claims.put("roles", roles);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()//
.setClaims(claims)//
.setIssuedAt(now)//
.setExpiration(validity)//
.signWith(SignatureAlgorithm.HS256, secretKey)//
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getUsername(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");
}
}
}
Given your context, the controller is responsible for issuing a new token (after validating credentials) while the filter is responsible for authenticating the user against the given token. The controller should not populate the security context (authenticate user), it is the filter's responsibility.
To better understand the two phases:
Spring uses two filters to authenticate and log in a user.
See UsernamePasswordAuthenticationFilter and SecurityContextPersistenceFilter in a "username/password" scenario, from the Spring Security project: the first one processes an authentication attempt (username/password) while the latter populates the security context from a SecurityContextRepository (from a session in general).

Failed to find access token for token

I use postMan, enter the request address http://localhost:8011/umrah/oauth/token?client_id=client_2&username=1234567&password=123456&grant_type=password&client_secret=123456, click the send button, an error occurs,It works fine in memory, when I want to use Jdbc token storage,Idea console error: failed to find access token for token,I found some information and did not find a suitable solution.
POSTMAN request params
oauth_client_token table is null
console error
#Configuration
#EnableWebSecurity
#Slf4j
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserService userService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.logout()
.clearAuthentication(true)
.and()
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/*", "/webjars/**", "/resources/**", "/swagger-ui.html"
, "/swagger-resources/**", "/v2/api-docs", "index.html", "/logout"
, "/swagger","/user/loginIn").permitAll()
.and()
.csrf()
.disable();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
#Configuration
public class OAuth2ServerConfig {
private static final String DEMO_RESOURCE_ID = "order";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.logout()
.clearAuthentication(true)
.and()
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
// .and()
// .requestMatchers().anyRequest()
.and()
.anonymous()
.and()
.authorizeRequests()
.antMatchers("/base/**", "/oauth/*", "/webjars/**", "/resources/**", "/swagger-ui.html"
, "/swagger-resources/**", "/v2/api-docs", "index.html", "/swagger/**","/user/loginIn").permitAll()
.anyRequest().authenticated()
.and()
.cors()
.and()
.csrf()
.disable();//配置order访问控制,必须认证过后才可以访问
// #formatter:on
}
}
#Configuration
#EnableAuthorizationServer
#Slf4j
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
AuthenticationManager authenticationManager;
#Autowired
RedisConnectionFactory redisConnectionFactory;
#Autowired
UserDetailsService userDetailsService;
// #Autowired
// #Qualifier("myMemoryTokenStore")
// TokenStore myTokenStore;
#Autowired
private DataSource dataSource;
#Bean // 声明TokenStore实现
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置两个客户端,一个用于password认证一个用于client认证
// clients.inMemory().withClient("client_1")
//// .resourceIds(DEMO_RESOURCE_ID)
// .authorizedGrantTypes("client_credentials")
// .scopes("select")
// .authorities("ROLE_ADMIN","ROLE_USER")
// .secret("123456")
// .and().withClient("client_2")
//// .resourceIds(DEMO_RESOURCE_ID)
// .authorizedGrantTypes("password", "refresh_token")
// .scopes("select")
// .accessTokenValiditySeconds(1800)
// .refreshTokenValiditySeconds(3600)
// .authorities("ROLE_ADMIN","ROLE_USER")
// .secret("123456");
clients.withClientDetails(clientDetails());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
// 2018-4-3 增加配置,允许 GET、POST 请求获取 token,即访问端点:oauth/token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
// 配置TokenServices参数
DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(true);
// 复用refresh token
tokenServices.setReuseRefreshToken(true);
tokenServices.setRefreshTokenValiditySeconds(3600);
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1)); // 1天
endpoints.tokenServices(tokenServices);
super.configure(endpoints);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
}
#FrameworkEndpoint
public class LogoutEndpoint {
#Qualifier("myMemoryTokenStore")
#Autowired
private TokenStore tokenStore;
#RequestMapping(value = "/oauth/logout", method= RequestMethod.POST)
#ResponseStatus(HttpStatus.OK)
public void logout(HttpServletRequest request, HttpServletResponse response){
String authHeader = request.getHeader("Authorization");
if (authHeader != null) {
String tokenValue = authHeader.replace("Bearer", "").trim();
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
tokenStore.removeAccessToken(accessToken);
}
}
}
}
#Service("userService")
#Slf4j
public class UserService implements UserDetailsService {
#Resource(name = "service.UserService")
private com.jolly.atplan.umrah.service.service.UserService userService;
#Override
public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {
log.info("LoginID : {}",loginId);
User user = userService.getUserByLoginId(loginId);
if(Objects.isNull(user)){
throw new UsernameNotFoundException("User " + loginId + " was not found in the database");
}
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
// List<UserAuthority> authorityList = userAuthorityDao.getAuthorityListByUser(loginId);
// for (UserAuthority authority : authorityList) {
// GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority.getAuthority());
// grantedAuthorities.add(grantedAuthority);
// }
//返回一个SpringSecurity需要的用户对象
return new org.springframework.security.core.userdetails.User(
user.getLoginId(),
user.getPwd(),
grantedAuthorities);
}
}
already at work,I override the JdbcTokenStore readAccessToken method,Thanks Rest service with oauth2: Failed to find access token for token
`public class JdbcTokenStores extends JdbcTokenStore {
private static final Log LOG = LogFactory.getLog(JdbcTokenStores.class);
public JdbcTokenStores(DataSource dataSource) {
super(dataSource);
}
#Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
OAuth2AccessToken accessToken = null;
try {
accessToken = new DefaultOAuth2AccessToken(tokenValue);
}
catch (EmptyResultDataAccessException e) {
if (LOG.isInfoEnabled()) {
LOG.info("Failed to find access token for token "+tokenValue);
}
}
catch (IllegalArgumentException e) {
LOG.warn("Failed to deserialize access token for " +tokenValue,e);
removeAccessToken(tokenValue);
}
return accessToken;
}
}`

Spring security configuration with JWT / antMatchers blocking access

We are setting up Spring Security within a 1.3 Spring Boot application. We have created a class to configure everything with Java config but for some reason every time I try to access to any of the URLS that are configured to "permitAll()" I get a message response similar to this one:
{
"timestamp": 1443099232454,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api/register"
}
I am not really sure why I get this if I am setting up the antMatchers to allow access to the registration, authentication and activation urls. If I disable those three lines I am able to access these three endpoints.
This is my current configuration:
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Inject
private Http401UnauthorizedEntryPoint authenticationEntryPoint;
#Inject
private UserDetailsService userDetailsService;
#Inject
private TokenProvider tokenProvider;
public SecurityConfig() {
super(true);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Inject
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
private JwtTokenConfigurer securityConfigurerAdapter() {
return new JwtTokenConfigurer(tokenProvider);
}
}
UserDetailsService.java
#Service("userDetailsService")
#Log4j2
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Inject
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(final String email) {
log.debug("Authenticating {}", email);
String lowercaseEmail = email.toLowerCase();
Optional<User> userFromDatabase = userRepository.findOneByEmail(lowercaseEmail);
return userFromDatabase.map(
user -> {
if (!user.isEnabled()) {
throw new DisabledException("User " + lowercaseEmail + " is disabled");
}
List<GrantedAuthority> grantedAuthorities = user.getRoles().stream()
.map(role -> role.getGrantedAuthority()).collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(lowercaseEmail, user.getPassword(),
grantedAuthorities);
}).orElseThrow(
() -> new UsernameNotFoundException("User " + lowercaseEmail + " was not found in the database"));
}
}
JwtTokenConfigurer.java
public class JwtTokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private TokenProvider tokenProvider;
public JwtTokenConfigurer(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
#Override
public void configure(HttpSecurity http) throws Exception {
JwtTokenFilter customFilter = new JwtTokenFilter(tokenProvider);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtTokenFilter.java
public class JwtTokenFilter extends GenericFilterBean {
private final static String JWT_TOKEN_HEADER_NAME = "Authorization";
private TokenProvider tokenProvider;
public JwtTokenFilter(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
try {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String jwtToken = httpServletRequest.getHeader(JWT_TOKEN_HEADER_NAME);
if (StringUtils.hasText(jwtToken)) {
String authorizationSchema = "Bearer";
if (jwtToken.indexOf(authorizationSchema) == -1) {
throw new InsufficientAuthenticationException("Authorization schema not found");
}
jwtToken = jwtToken.substring(authorizationSchema.length()).trim();
JwtClaims claims = tokenProvider.parseToken(jwtToken);
String email = (String) claims.getClaimValue(TokenConstants.EMAIL.name());
List<GrantedAuthority> grantedAuthorities = claims.getStringListClaimValue(TokenConstants.ROLES.name())
.stream().map(role -> new SimpleGrantedAuthority(role)).collect(Collectors.toList());
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
email, null, grantedAuthorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
filterChain.doFilter(servletRequest, servletResponse);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Http401UnauthorizedEntryPoint.java
#Component
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)
throws IOException, ServletException {
log.debug("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
As I mentioned before, every time I try to access any of these three endpoints:
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
I get access denied... Any ideas?
You need to allow anonymous users.
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()
.and()
.anonymous()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.apply(securityConfigurerAdapter());
// #formatter:on
}
Because AbstractSecurityInterceptor always asks if there is something in the SecurityContextHolder.
AbstractSecurityInterceptor#beforeInvocation line 221
if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound","An Authentication object was not found in the SecurityContext"),object, attributes);
}

Categories

Resources