@WassupB

Как при удачной авторизации изменить кнопку Log In на кнопку Log Out и скрыть кнопку от обычных пользователей?

Читал кучу гайдов, видео, пытался разобраться так и не получилось, видимо из-за недостаточного опыта попадаешь в тупик, chatGPT не помог.

<a class="nav-link" th:href="@{/users}">List of Users</a>


Ни в какую не работает этот код:

<sec:authorize access="isAuthenticated()">
   <!-- Content for Authenticated users -->  
</sec:authorize>


Что бы скрыть страницу администратора /users не работает этот код
<div sec:authorize="hasRole('ADMIN')">
    This content is only shown to administrators.
</div>


Перепробовал все варианты, смотрел исходники чужих проектов, результат один.

Я вижу и под юзером, и под админом, и под гостем, перепробовал множество вариантов и результат не оказывается тем, который ожидаешь, отсюда у меня появилась мысль, что проблема возможно лежит где-то в другом месте, возможно опытным путем кто-то поможет найти и решить проблемы с правами доступа?

P.S. остальные механизмы работают, например .../users/ может просматривать только админ, так и есть, обычный юзер сделать этого не может.

Вот мой код. Либо ссылка на проект https://github.com/CookieVortex/SpringBoot

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().and()
                .authorizeRequests()
                .antMatchers("/", "/register").permitAll()
                .antMatchers("/users", "/index").hasAnyAuthority("ADMIN")
                .anyRequest().authenticated() // Require authentication for all other requests
                .and()
                .formLogin()
                .loginPage("/login")
                .failureUrl("/login?error")
                .usernameParameter("email")
                .defaultSuccessUrl("/")
                .permitAll()
                .and()
                .logout()
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login?logout")
                .deleteCookies("my-remember-me-cookie")
                .permitAll()
                .and()
                .rememberMe()
                //.key("my-secure-key")
                .rememberMeCookieName("my-remember-me-cookie")
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(24 * 60 * 60)
                .and()
                .exceptionHandling();
    }


@Controller
public class AppController {

    private final UserRepository userRepo;

    public AppController(UserRepository userRepo) {
        this.userRepo = userRepo;
    }

    @GetMapping("")
    public String viewHomePage(Model model) {
        List<User> listUsers = userRepo.findAll();
        model.addAttribute("listUsers", listUsers);
        return "index";
    }

    @GetMapping("/register")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new User());

        return "signup_form";
    }

    @PostMapping("/process_register")
    public String processRegister(User user) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodedPassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodedPassword);

        user.setRole("USER");
        userRepo.save(user);

        return "register_success";
    }

    @GetMapping("/users")
    public String listUsers(Model model) {
        List<User> listUsers = userRepo.findAll();
        model.addAttribute("listUsers", listUsers);
        return "users";
    }


    @GetMapping("/login")
    public String login() {
        return "login";
    }
}


@Entity
@Table(schema = "dev", name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "username", nullable = false, unique = true, length = 45)
    private String email;

    @Column(name = "password", nullable = false, length = 64)
    private String password;

    @Column(name = "first_name", nullable = false, length = 20)
    private String firstName;

    @Column(name = "last_name", nullable = false, length = 20)
    private String lastName;

    @Column(name = "role", nullable = false, length = 10)
    private String role;

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = 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 getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}


public interface UserRepository extends JpaRepository<User, Long> {
	@Query("SELECT u FROM User u WHERE u.email = ?1")
	public User findByEmail(String email);
	
}


public class CustomUserDetails implements UserDetails {

	private User user;
	
	public CustomUserDetails(User user) {
		this.user = user;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return Collections.singleton(new SimpleGrantedAuthority(user.getRole()));
	}

	@Override
	public String getPassword() {
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		return user.getEmail();
	}

	@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 String getFullName() {
		return user.getFirstName() + " " + user.getLastName();
	}

}


@Service
public class CustomUserDetailsService implements UserDetailsService {

	@Autowired
	private UserRepository userRepo;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userRepo.findByEmail(username);
		if (user == null) {
			throw new UsernameNotFoundException("User not found");
		}
		return new CustomUserDetails(user);
	}

}


<header class="header">
    <ul class="nav nav-pills justify-content-end">
        <li class="nav-item">
            <a class="nav-link custom-style text-white bg-primary" th:href="@{/register}">Sign Up</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" th:href="@{/login}">Log In</a>

        </li>
        <div sec:authorize="hasRole('ADMIN')">
            <li class="nav-item">
                <a class="nav-link" th:href="@{/users}">List of Users</a>
            </li>
        </div>
    </ul>
</header>
  • Вопрос задан
  • 137 просмотров
Пригласить эксперта
Ответы на вопрос 1
@My1Name
Вам нужен CustomAuthenticationProvider, а не CustomUserDetails. Там делегируются права (role) пользователям при аутентификации.

Вот это вот лишнее:
@PostMapping("/process_register")
    public String processRegister(User user) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String encodedPassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodedPassword);
        user.setRole("USER");
        userRepo.save(user);
        return "register_success";
    }

Возможно имеет смысл в экспериментальных (в учебных) целях, но на практике это лишнее... PasswordEncoder достаточно добавить 1 раз через RegisterGlobalAuthentication в классе WebSecurityConfig; а также добавив соответствующий бин:
@Bean
public PasswordEncoder pass(){
return new BCryptPasswordEncoder();
}

После регистрации, если она прошла успешно, обычно делается autoLogin через SecurityContextHolder и return "redirect:/index";

Если вы хотите шифровать данные в БД, то имеет смысл вынести BCryptPasswordEncoder() в глобальную переменную (в final поле) на уровне класса - контроллера.

Смысл PasswordEncoder() в Spring Security заключается в том, что на время сессии создаётся "хэш-отпечаток" (цифровая подпись) кроме прочей информации в памяти... Его в принципе невозможно декодировать не зная реальный пароль и правила преобразования. Поэтому без него Spring Security и не работает.

Вот старая модель CHA-1 кодирования в соответствии со спецификацией Oracle к PasswordEncoder. А BCryptPasswordEncoder использует более новые и более продуманные алгоритмы.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы