Spring Boot security sign-in 403 Forbidden error question - spring-boot

I am having some issue on my Spring security sign-in. Signup works fine with no error but only sign-in returns 403 forbidden error.
I tried add http.httpBasic() and it returns 401 error.
I have http.csrf().disable() in the SecurityConfig.java but it still doesn't work even it's permitAll() condition. I am stuck in this problem for days :/ I tried every single solution that I googled but nothing worked.
Here is SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();//cross-origin-resource-sharing
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/api/authentication/**").permitAll()//login and register pre-path
.anyRequest().permitAll();
http.addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public JwtAuthorizationFilter jwtAuthorizationFilter()
{
return new JwtAuthorizationFilter();
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer()
{
#Override
public void addCorsMappings(CorsRegistry registry)
{
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*");
}
};
}
I think from this jwtAutheorizationFiler.java cause the issue if the Security config is fine:
public class JwtAuthorizationFilter extends OncePerRequestFilter
{
#Autowired
private JwtProvider jwtProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
Authentication authentication = jwtProvider.getAuthentication(request);
if (authentication != null && jwtProvider.isTokenValid(request))
{
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
SecurityUtils.java
public class SecurityUtils
{
public static final String ROLE_PREFIX = "ROLE_";
public static final String AUTH_HEADER = "authorization";
public static final String AUTH_TOKEN_HEADER = "Bearer";
public static final String AUTH_TOKEN_PREFIX = AUTH_TOKEN_HEADER + " ";
public static SimpleGrantedAuthority convertToAuthority(String role)
{
String formattedRole = role.startsWith(ROLE_PREFIX) ? role : ROLE_PREFIX + role;
return new SimpleGrantedAuthority(formattedRole);
}
public static String extractAuthTokenFromRequest(HttpServletRequest request)
{
String bearerToken = request.getHeader(AUTH_HEADER);
if(StringUtils.hasLength(bearerToken) && bearerToken.startsWith(AUTH_TOKEN_PREFIX))
{
return bearerToken.substring(7);
}
return null;
}
}
CustomUserDetailService.java :
#Service
public class CustomUserDetailsService implements UserDetailsService
{
private LoginService loginService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Login login = loginService.findByUsername(username)
.orElseThrow(()-> new UsernameNotFoundException("User not found with username: "+ username));
Set<GrantedAuthority> authorities = Set.of(SecurityUtils.convertToAuthority(login.getRole().name()));
return UserPrincipal.builder()
.login(login)
.id(login.getId())
.username(login.getUsername())
.password(login.getPassword())
.authorities(authorities)
.build();
}
}
AuthenticationController.java
#Autowired
private AuthenticationService authenticationService;
#Autowired
private LoginService loginService;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#PostMapping("sign-up")//api/authentication/sign-up
public ResponseEntity<?> signUp(#RequestBody Login login)
{
if(loginService.findByUsername(login.getUsername()).isPresent())
{
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
return new ResponseEntity<>(loginService.saveLogin(login), HttpStatus.CREATED);
}
#PostMapping("sign-in")//api/authentication/sign-in
public ResponseEntity<?> signIn(#RequestBody Login login)
{
return new ResponseEntity<>(authenticationService.signInAndReturnJWT(login), HttpStatus.OK);
}
AuthenticationServiceImple.java
#Service
public class AuthenticationServiceImpl implements AuthenticationService
{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtProvider jwtProvider;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#Override
public Login signInAndReturnJWT(Login signInRequest)
{
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword())
);
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
String jwt = jwtProvider.generateToken(userPrincipal);
Login signInUser = userPrincipal.getLogin();
signInUser.setAccessToken(jwt);
signInUser.setRefreshToken(jwtRefreshTokenService.createRefreshToken(signInUser.getId()).getTokenId());
return signInUser;
}
}

Related

Get Forbidden error in spring security role based authentication and authorization

I am trying to learn Spring security where I am facing a problem with Role based security.
There are 2 tables User and Role having One to Many realtions.
When I enter username and password in spring default form, I successfully fetch correct Users deatails through loadUserByUserName() method. But on screen I get a message like
This application has no explicit mapping for /error, so you are seeing
this as a fallback.
There was an unexpected error (type=Forbidden, status=403). Forbidden
Only #GetMapping("/user") method works properly.
Here is the controller part
#RestController
#RequestMapping("/admin")
public class AdminController {
#Autowired
UserRepository userRepo;
#Autowired
RoleRepository roleRepo;
#PreAuthorize("hasAnyRole('ADMIN')")
#PostMapping("/add")
public String addUserByAdmin(#RequestBody User user)
{
user.getRoles().forEach(role -> role.setUser(user));
userRepo.save(user);
return "User added Successfully";
}
#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping("/process")
public String process()
{
return "Processing....";
}
#GetMapping("/user")
public String users() // This code is working properly
{
System.out.println("U r in user area's");
return "User's space";
}
}
Configuration part
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
protected void configure(HttpSecurity http)throws Exception
{
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/admin/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
}
Service part
#Service
public class CustomeUserDetailsService implements UserDetailsService
{
#Autowired
UserRepository userRepo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User u = userRepo.findByName(username);
CustomeUserDetails cUserDetails = new CustomeUserDetails();
if(u == null)
{
throw new UsernameNotFoundException("User "+username +"not found");
}
else
{
cUserDetails.setUser(u);
}
return cUserDetails;
}
}
Where am I wrong ?
What if I want to add one more URL which does not require any authnetication and authorization , how to add it ?
I think you should use "ROLE_ADMIN" instead of "ADMIN". The "ROLE_ADMIN" key can be saved in the database and turned into Collection. Below how I did.
CLASS THAT IMPLEMENTS USERDETAILS
public class UsuarioSS implements LdapUserDetails {
private static final long serialVersionUID = 1164806375870272028L;
private String cdusuariorede;
private Collection<GrantedAuthority> authorities;
public UsuarioSS() {
}
public UsuarioSS(String cdusuariorede,List<Perfil> perfis) {
super();
this.cdusuariorede = cdusuariorede;
this.authorities = new ArrayList<GrantedAuthority>();
for (Perfil perfil : perfis) {
this.authorities.add(new SimpleGrantedAuthority(perfil.toString()));
}
}
public String getId() {
return cdusuariorede;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return null;
}
#Override
public String getUsername() {
return cdusuariorede;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public boolean hasRole(String springSecurityKey) {
return getAuthorities().contains(new SimpleGrantedAuthority(springSecurityKey));
}
#Override
public void eraseCredentials() {
// TODO Auto-generated method stub
}
#Override
public String getDn() {
// TODO Auto-generated method stub
return null;
}
}
ENUM THAT REPRESENTS MY USER PROFILES (AUTHORITHYES)
public enum Perfil {
ROLE_ADMIN,ROLE_DEFAULT
}
A AUTHORIZATION FILTER
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private JWTUtil jwtUtil;
private UserDetailsService userDetailsService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil,
UserDetailsService userDetailsService) {
super(authenticationManager);
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
UsernamePasswordAuthenticationToken auth = getAuthentication(header.substring(7));
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(String token) {
if (jwtUtil.tokenValido(token)) {
String login = jwtUtil.getLogin(token);
UserDetails usuario = userDetailsService.loadUserByUsername(login);
return new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
}
return null;
}
}
MY CONTROLLER
#RestController
#RequestMapping(value = "/parte")
public class ParteController {
#Autowired
private ParteService service;
#PreAuthorize("hasAnyRole('ROLE_ADMIN')")
#GetMapping("/partes_demandadas_por_processo")
public ResponseEntity<List<TpPartesProcessoDto>> getPartesPorNuprocesso(
#RequestParam(name = "processo",required = true)
#Length(max = 15,min = 15,message = "O campo processo deve possuir 15 caracteres.")
String processo
) throws SQLException{
List<TpPartesProcessoDto> partes = service.getPartesdoProcessoPorNuprocesso(processo);
return ResponseEntity.ok().body(partes);
}
}

Spring Boot Jwt returns access denied

Hey everyone i have problem with jwt with Java.Here is the codes.
Here is returned value from postman
{
"timestamp": "2020-02-29T20:53:35.761+0000",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/login"
}
TokenManager.java
#Service
public class TokenManager {
private static final int expiredAt = 10 * 60 * 60 * 1000;
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
public String generateToken(String username){
return Jwts.builder().setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiredAt))
.signWith(key).compact();
}
public boolean tokenValidate(String token){
if(getUserFromToken(token) != null && isExpired(token)) {
return true;
}
return false;
}
public String getUserFromToken(String token){
Claims claims = getClaims(token);
return claims.getSubject();
}
public boolean isExpired(String token){
Claims claims = getClaims(token);
return claims.getExpiration().after(new Date(System.currentTimeMillis()));
}
private Claims getClaims(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
And then JwtTokenFilter.java
#Component
public class JwtTokenFilter extends OncePerRequestFilter {
#Autowired
private TokenManager tokenManager;
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
#NotNull HttpServletResponse httpServletResponse,
#NotNull FilterChain filterChain) throws ServletException, IOException {
final String authHeader = httpServletRequest.getHeader("Authorization");
String username = null;
String token = null;
if (authHeader != null && authHeader.contains("Bearer")) {
token = authHeader.substring(7);
try {
username = tokenManager.getUserFromToken(token);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
if (username != null && token != null
&& SecurityContextHolder.getContext().getAuthentication() == null) {
if (tokenManager.tokenValidate(token)) {
UsernamePasswordAuthenticationToken upassToken =
new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
upassToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(upassToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
And my custom UserDetailService
#Service
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username);
}
}
Here is WebSecurityConfig
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtTokenFilter tokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/signup","/login").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
public AuthenticationManager getAuthenticationManager() throws Exception {
return super.authenticationManagerBean();
}
}
And last one is my controller.I checked the request body and and print the data it just work fine but /login path returns access denied.
#RestController
public class UserController {
#Autowired
private UserService userService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenManager tokenManager;
public UserController(UserService userService, AuthenticationManager authenticationManager, TokenManager tokenManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
}
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public ResponseEntity<User> signup(#RequestBody User user){
return ResponseEntity.ok(userService.save(user));
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(#Valid #RequestBody AuthRequest authRequest){
try{
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getUsername(),authRequest.getPassword()));
return ResponseEntity.ok(tokenManager.generateToken(authRequest.getUsername()));
}catch (Exception e){
throw e;
}
}
}
When I remove authenticationManager.authenticate method inside login function it returns a valid token.But when I add authenticationManager again it returns access denied.
Actually you did not setup the AuthenticationManager properly.
in your code, you just used the default authentication manager. And it is ok, as there is one default implementation shipped in Spring boot security, which is ProviderManager. what [ProviderManager][1] does is:
Iterates an Authentication request through a list of AuthenticationProviders.
So you need at least one AuthenticationProvider
There are quite some AuthenticationProviders, for example:
AnonymousAuthenticationProvider, NullAuthenticationProvider, DaoAuthenticationProvider, LdapAuthenticationProvider etc
And in your case, you are authenticating against database, so the DaoAuthenticationProvider is the choice.
And Spring security has a very easy way to configure the DaoAuthenticationProvider, and actually, it automatically created one for you when you set userDetailsService to the AuthenticationManagerBuilder to configure your AuthenticationManager, code like this:
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
so all you need to do is add the code snipet above to your WebSecurityConfig
And it is also recommended to use PasswordEncoder instead of storing your password as plain text. A simple way is to use BCryptPasswordEncoder to encode your password before save the user to db...
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}

How to get a username from post method in spring security?

I am using spring-boot and spring-security in app. My goal is to get the user name of the currently registered user from post method. Get method is working nicely but the post method isn't working. Why? How can I solve this problem?
Test Controller
#GetMapping("/test")
public String test(Authentication authentication) {
System.out.println(authentication.getName()); // <--------- It's working
return "testfile";
}
#PostMapping("/test")
public String testPost(Authentication authentication) {
System.out.println(authentication.getName()); // <--------- NOLL ERROR!
return "testfile";
}
Error
java.lang.NullPointerException: null
User
#Entity
#Table(name="user")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="mail")
private String mail;
#Column(name="password")
private String password;
}
UserDAO
#Repository
public class UserDAO {
#Autowired
private EntityManager entityManager;
public List<User> findAll() {
return entityManager.unwrap(Session.class).createQuery("from User", User.class).getResultList();
}
public User findByMail(String mail){
Session currentSession = entityManager.unwrap(Session.class);
Query theQuery = currentSession.createQuery("from User where mail=:mail", User.class);
theQuery.setParameter("mail", mail);
List<User> users = theQuery.getResultList();
if(users.isEmpty()){
return new User();
}
return users.get(0);
}
public void saveOrUpdate(User user) {
Session currentSession = entityManager.unwrap(Session.class);
currentSession.saveOrUpdate(user);
}
}
UserService
public interface UserService extends UserDetailsService{
public List<User> findAll();
public User findByMail(String mail);
public void saveOrUpdate(User user);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService{
#Autowired
private UserDAO userDAO;
#Autowired
private UserRoleDAO userRoleDAO;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Override
#Transactional
public List<User> findAll() {
return userDAO.findAll();
}
#Override
#Transactional
public User findByMail(String mail){
return userDAO.findByMail(mail);
}
#Override
#Transactional
public void saveOrUpdate(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
userDAO.saveOrUpdate(user);
}
#Override
#Transactional
public UserDetails loadUserByUsername(String mail) throws UsernameNotFoundException {
User user = userDAO.findByMail(mail);
List<UserRole> userRole = userRoleDAO.findByUserId(user.getId());
if (user == null) {
throw new UsernameNotFoundException("Invalid username or password.");
}
return new org.springframework.security.core.userdetails.User(user.getName(), user.getPassword(), mapRolesToAuthorities(userRole));
}
private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<UserRole> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getRole())).collect(Collectors.toList());
}
}
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private DataSource dataSource;
#Autowired
private UserService userService;
RedirectAuthenticationSuccessHandler redirectAuthenticationSuccessHandler = new RedirectAuthenticationSuccessHandler();
RedirectAuthenticationFailureHandler redirectAuthenticationFailureHandler = new RedirectAuthenticationFailureHandler();
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(env.getProperty("my.usersbyusernamequery"))
.authoritiesByUsernameQuery(env.getProperty("my.authoritiesbyusernamequery"));
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/anypage1/**").hasRole("MANAGER")
.antMatchers("/anypage2/**").hasRole("ADMIN")
.antMatchers("/test").hasRole("ADMIN")
.authenticated()
.antMatchers("/**").permitAll()
.and()
.formLogin().loginPage("/login").failureHandler(redirectAuthenticationFailureHandler)
.loginProcessingUrl("/login-control").successHandler(redirectAuthenticationSuccessHandler).permitAll()
.and()
.logout().logoutUrl("/logout").permitAll().and().exceptionHandling().accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.POST, "/anypage3").antMatchers(HttpMethod.POST, "/anypage4")
.antMatchers(HttpMethod.POST, "/test");
}
#Bean
public BCryptPasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
auth.setUserDetailsService(userService);
auth.setPasswordEncoder(passwordEncoder());
return auth;
}
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.POST, "/anypage3").antMatchers(HttpMethod.POST, "/anypage4")
.antMatchers(HttpMethod.POST, "/test");
}
You ignore /test in post method, so it will not be filtered by spring security filter, try to remove this.
You can get username from SecurityContextHolder
User user =
(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String name = user.getUsername(); //get current logged in username
In loadUserByUsername method you can manually set the Authentication token on SecurityContextHolder and same you can use in controller
UsernamePasswordWithAttributesAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( loadUserByUsername(username), password, authorities );
SecurityContextHolder.getContext().setAuthentication(authenticationToken);

Spring security with JWT always returns 401 unauthorized

1
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Resource(name = "userService")
private UserDetailsService userDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
/*auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select login as principal, mot_de_passe as credentials, flag_compte_actif as enabled from utilisateur where login = ?")
.authoritiesByUsernameQuery("SELECT utilisateur.login as principal, profil.designation as role FROM utilisateur INNER JOIN user_profil ON utilisateur.id_user = user_profil.iduserpk INNER JOIN profil ON user_profil.idprofilpk = profil.id_profil WHERE utilisateur.login = ? ")
.rolePrefix("ROLE_");
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());*/
auth.inMemoryAuthentication()
.withUser("admin")
.password("password")
.roles("Administrateur");
}
#Bean
public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationFilter();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("Akal configure method begin");
//http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http.cors().and()
.csrf().disable().
authorizeRequests()
.antMatchers("/token/generate").permitAll()
.anyRequest().authenticated()
.and().formLogin().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
System.out.println("Akal configure method");
http
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
// #Bean
// public BCryptPasswordEncoder passwordEncoder(){
// return new BCryptPasswordEncoder();
// }
#Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
2
#RestController
#CrossOrigin("*")
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private UtilisateurRepository userRepo;
#PostMapping(value = "/token/generate")
public ResponseEntity<?> register(#RequestBody LoginUser loginUser) throws AuthenticationException {
System.out.println("We're in man!");
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginUser.getUsername(),
loginUser.getPassword()
)
);
System.out.println("(Username, Password): (" + loginUser.getUsername() + ", " + loginUser.getPassword() + ")");
SecurityContextHolder.getContext().setAuthentication(authentication);
final Utilisateur user = userRepo.findByLogin(loginUser.getUsername());
final String token = jwtTokenUtil.generateToken(user);
System.out.println("Token Controller Access=> Token Generated: " + token);
return ResponseEntity.ok(new AuthToken(token));
}
}
3
public class AuthToken {
private String token;
public AuthToken(){
}
public AuthToken(String token){
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
4
public class CorsFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
System.out.println("Filtering on...........................................................");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
//response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization, Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
5
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
6
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
String header = req.getHeader("Authorization");
String username = null;
String authToken = null;
if (header != null && header.startsWith("Bearer ")) {
authToken = header.replace("Bearer ","");
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
} catch (IllegalArgumentException e) {
logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
logger.warn("the token is expired and not valid anymore", e);
} catch(SignatureException e){
logger.error("Authentication Failed. Username or Password not valid.");
}
} else {
logger.warn("couldn't find bearer string, will ignore the header");
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(req));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(req, res);
}
}
7
#Component
public class JwtTokenUtil implements Serializable {
static final long EXPIRATIONTIME = 864_000_000; // 10 days
static final String SECRET = "secret";
static final String TOKEN_PREFIX = "Bearer";
static final String HEADER_STRING = "Authorization";
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(Utilisateur user) {
return doGenerateToken(user.getLogin());
}
private String doGenerateToken(String subject) {
Claims claims = Jwts.claims().setSubject(subject);
claims.put("scopes", Arrays.asList(new SimpleGrantedAuthority("ROLE_Administrateur")));
return Jwts.builder()
.setClaims(claims)
.setIssuer("http://devglan.com")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (
username.equals(userDetails.getUsername())
&& !isTokenExpired(token));
}
}
8
public class LoginUser {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
I only posted these 2 classes, because honestly I have 8 configuration classes, it's gonna be a pain to read! And it's custom JWT code too, but if it's necessary to post it all, let me know.
Other than that, I just cannot identify the problem! Spring console doesn't show any errors whatsoever and when I try to request from Postman, here the outcome:
result
And when I run the request from the browser, it doesn't say 401, it just says bad credentials even though they're correct and I tried with dozens of users too to make sure
Thank you!
Update: I posted the rest of the classes because the problem might not be related to just these 2
In Spring Security 5, if you are using auth.inMemoryAuthentication(), you won't be able to use BCryptPasswordEncoder or StandardPasswordEncoder. You must use your own UserDetailsService in order to get a user and password. Or if you need to test your code, just return NoOpPasswordEncoder.getInstance() in your passwordEncoder() method
SecurityConfig.class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AccountService accountService; //your own implementation of UserDetailsService
....
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.eraseCredentials(true)
.userDetailsService(accountService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
....
}
AccountService.class
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class AccountService implements UserDetailsService {
#Autowired
private AccountRepository accountRepository; //Your database repository
#Autowired
private PasswordEncoder passwordEncoder;
#PostConstruct
protected void initialize() {
save(new Account("user", "demo", "ROLE_USER"));
save(new Account("admin", "admin", "ROLE_ADMIN"));
}
#Transactional
public Account save(Account account) {
account.setPassword(passwordEncoder.encode(account.getPassword()));
accountRepository.save(account);
return account;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountRepository.findOneByEmail(username);
if(account == null) {
throw new UsernameNotFoundException("user not found");
}
return createUser(account);
}
public void signin(Account account) {
SecurityContextHolder.getContext().setAuthentication(authenticate(account));
}
private Authentication authenticate(Account account) {
return new UsernamePasswordAuthenticationToken(createUser(account), null, Collections.singleton(createAuthority(account)));
}
private User createUser(Account account) {
return new User(account.getEmail(), account.getPassword(), Collections.singleton(createAuthority(account)));
}
private GrantedAuthority createAuthority(Account account) {
return new SimpleGrantedAuthority(account.getRole());
}
}
Account.class
#SuppressWarnings("serial")
#Entity
#Table(name = "account")
public class Account implements java.io.Serializable {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String email;
#JsonIgnore
private String password;
private String role = "ROLE_USER";
private Instant created;
protected Account() {
}
public Account(String email, String password, String role) {
this.email = email;
this.password = password;
this.role = role;
this.created = Instant.now();
}
public Long getId() {
return id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Instant getCreated() {
return created;
}
}
You are not using PasswordEncoder in your globalUserDetails() method. Spring security by default take encoded password. Your code should be like.
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder)
.withUser("admin")
.password("password")
.roles("Admin");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
You are using do not need dataSource because you are using inMemoryAuthenticatin().

ClientDetailsService In Memory don't work

I changed OAUTH2 version from 2.0.3 to 2.0.14 and Authorization Server is not working.
I had this message from server:
o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: InsufficientAuthenticationException, There is no client authentication. Try adding an appropriate authentication filter.
Could you help me and say what is wrong ? I have token storage in database and I want to use ClientDetailsService from memory, but Spring not recognized this.
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends
AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS =
"tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
private AuthenticationManager authenticationManager;
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws
Exception {
security.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws
Exception {
clients.inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID)).scopes("read",
"write").authorities(Authorities.ROLE_ADMIN.name(),
Authorities.ROLE_USER.name())
.authorizedGrantTypes("password",
"refresh_token").secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(
propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class,
1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment,
ENV_OAUTH);
}
}
Security configuration
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsComponent;
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws
Exception {
auth
.userDetailsService(userDetailsComponent)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/register").antMatchers("/console/*")
.antMatchers("/oauth/**");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Method security configuration.
#EnableGlobalMethodSecurity(prePostEnabled = true,
proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration
{
}
Custom UserDetailsService.
#Component("userDetailsComponent")
public class UserDetailsComponent implements UserDetailsService {
private final Logger log =
LoggerFactory.getLogger(UserDetailsComponent.class);
#Autowired
private UsersRepository usersRepository;
#Override
public UserDetails loadUserByUsername(String login) {
log.debug("Authenticating {}", login);
Users userFromDatabase = null;
if (login.contains("#")) {
userFromDatabase = usersRepository.findByEmail(login);
} else {
userFromDatabase = usersRepository.findByUsername(login);
}
if (userFromDatabase == null) {
throw new UsernameNotFoundException("User " + login + " was not
found in the database");
} else if (!userFromDatabase.getActivated()) {
throw new UserNotActivatedException("User " + login + " is not
activated");
}
Collection<GrantedAuthority> grantedAuthorities = new
ArrayList<GrantedAuthority>();
for (OauthAuthority authority : userFromDatabase.getOauthAuthorities())
{
GrantedAuthority grantedAuthority = new
SimpleGrantedAuthority(authority.getAuthority().getRole());
grantedAuthorities.add(grantedAuthority);
}
return new User(userFromDatabase.getUsername(),
userFromDatabase.getPassword(), grantedAuthorities);
}
}
Properties
authentication.oauth.clientid=game
authentication.oauth.secret=secret
authentication.oauth.tokenValidityInSeconds=2000

Resources