我有一个实现 spring 安全的 spring boot 应用程序。在应用程序中,有一个导航栏和在 thymeleaf 中应用的片段。登录后,我无法使用导航栏访问给定的 url。我不知道为什么会这样。是否是由于 html 错误或 spring security 或地址故障。我想在登录后查看 userdetails.html 做什么。我的主页有登录部分。正确重定向到 uprojects.html。但是使用导航栏链接我无法检索“uprojects”和“userdetails”。任何帮助将不胜感激。我的代码如下,
最后,我收到错误“whitelabel error page”404。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
<title>CrossCheck</title>
</head>
<body>
<div th:fragment="header">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="#">
<img src="/assets/img/crosscheck_navbar_logo.png" width="30" height="30" alt="">
</a>
<a class="navbar-brand" th:href="@{/home}">CROSSCHECK</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-item nav-link active" th:href="@{/home}">Home <span class="sr-only">(current)</span></a>
<a class="nav-item nav-link" th:href="@{/nfeatures}">Features</a>
<a class="nav-item nav-link" th:href="@{/pricing}">Pricing</a>
<a sec:authorize="isAuthenticated()" class="nav-item nav-link" th:href="@{/uprojects}">Projects</a>
<a sec:authorize="isAuthenticated()" class="nav-item nav-link" th:href="@{/userdetails}">User</a>
<a sec:authorize="isAuthenticated()" class="nav-item nav-link" th:href="@{/logout}">Logout</a>
</div>
</div>
</nav>
</div>
</body>
</html>
安全设置如下,
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
SecurityFilterChain defaultSecurityFilterChain (HttpSecurity http) throws Exception{
http.authorizeHttpRequests((requests) -> requests
.requestMatchers("/registration/**").permitAll()
.requestMatchers("/home/**").permitAll()
.requestMatchers("/fragments/**").permitAll()
.requestMatchers("/nfeatures/**").permitAll()
.requestMatchers("/pricing/**").permitAll()
.requestMatchers("/assets/**").permitAll()
.requestMatchers("/forgotpass/**").permitAll()
.requestMatchers("/resetpass/**").permitAll()
.requestMatchers("/user/**", "/uprojects/**", "/userdetails/**").hasAnyRole("ROLE_USER", "ROLE_ADMIN")
.requestMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/home")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/uprojects")
.usernameParameter("email")
.permitAll()
)
.logout((logout) -> logout
.logoutSuccessUrl("/home")
.invalidateHttpSession(true)
.permitAll())
.exceptionHandling().accessDeniedPage("/access-denied");
return http.build();
}
}
我想在使用导航栏登录后查看的用户详细信息页面。
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN"
crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="/assets/js/jquery.input-allow.js"></script>
<title>User Details</title>
</head>
<body>
<div th:replace="~{fragments/header :: header}"></div>
<div class="container">
<div class="row mt-5 align-items-center">
<div class="d-flex flex-row justify-content-center">
<img th:src="@{/assets/img/crosscheck_login_logo.png}" class="img-fluid mx-auto">
</div>
<div class="card-body register-card-body">
<div class="mx-auto" style="width: 400px;">
<form
method="post"
role="form"
th:action="@{/userdetails}"
th:object="${userd}"
>
<div class="alert alert-success" role="alert" th:if="${registrationMsg != null}">
<p th:text="${registrationMsg}"></p>
</div>
<div class="alert alert-warning" role="alert" th:if="${#fields.hasErrors('global')}">
<p th:each="err : ${#fields.errors('global')}" th:text="${err}"></p>
</div>
<div th:if="${registrationMsg == null}">
<p class="login-box-msg"><b>Update your details</b></p>
<div class="mb-3">
<label class="col-sm-2 col-form-label col-form-label-sm" for="name">Name</label>
<input class="form-control" id="name" name="name" type="text" th:value="${name}">
<p class="text-danger" th:errors="*{name}"
th:if="${#fields.hasErrors('name')}"></p>
</div>
<div class="mb-2">
<label class="col-sm-2 col-form-label col-form-label-sm" for="surname">Surname</label>
<input class="form-control" id="surname" name="surname" type="text" th:value="${surname}">
<p class="text-danger" th:errors="*{surname}"
th:if="${#fields.hasErrors('surname')}"></p>
</div>
<div class="mb-2">
<label class="col-sm-2 col-form-label col-form-label-sm" for="country">Country</label>
<select name="country" id="country" class="form-select form-select-mb">
<option value="" th:selected="${country}"></option>
</select>
<p class="text-danger" th:errors="*{country}"
th:if="(${'country'} == 'Select country') OR ${#fields.hasErrors('country')}"></p>
</div>
<div class="mb-2">
<label class="col-mb-2 col-form-label col-form-label-sm" for="idNumber">Turkish National ID (Other countries, 11111111111)</label>
<input class="form-control inputfilter" id="idNumber" name="idNumber" type="text" maxlength="11" allow="[0-9]" th:value="${idNumber}">
<p class="text-danger" th:errors="*{idNumber}"
th:if="${#fields.hasErrors('idNumber')}"></p>
</div>
<label class="col-mb-2 col-form-label col-form-label-sm" for="gsmNumber">Mobile Phone</label>
<div class="input-group sm-3">
<span class="input-group-text" id="code"></span>
<input class="form-control inputfilter" id="gsmNumber" name="gsmNumber" type="text" maxlength="13" allow="[0-9]" th:value="${gsmNumber}">
<p class="text-danger" th:errors="*{gsmNumber}"
th:if="${#fields.hasErrors('gsmNumber')}"></p>
</div>
<div class="mb-2">
<label class="col-sm-2 col-form-label col-form-label-sm" for="regAdress">Adress</label>
<textarea class="form-control" id="regAdress" name="regAdress" rows="3" th:value="${regAdress}"></textarea>
<p class="text-danger" th:errors="*{regAdress}"
th:if="${#fields.hasErrors('regAdress')}"></p>
</div>
<div class="mb-2">
<label class="col-sm-2 col-form-label col-form-label-sm" for="zipcode">Zipcode</label>
<input class="form-control inputfilter" id="zipcode" name="zipcode" type="text" maxlength="8" allow="[0-9]" th:value="${zipcode}">
<p class="text-danger" th:errors="*{zipcode}"
th:if="${#fields.hasErrors('zipcode')}"></p>
</div>
<div class="mb-2">
<label class="col-mb-2 col-form-label col-form-label-sm" for="city">City or State</label>
<input class="form-control" id="city" name="city" type="text" th:value="${city}">
<p class="text-danger" th:errors="*{city}"
th:if="${#fields.hasErrors('city')}"></p>
</div>
<div class="mb-2">
<button class="btn btn-secondary mb-2" type="submit">Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<footer th:replace="~{fragments/footer :: footer}"></footer>
</body>
<script>
$(document).ready(function () {
var countryOptions = '';
$.getJSON('/assets/json/countries.json', function (data) {
countryOptions += '<option value="">Select country</option>';
$.each(data, function (key, country) {
countryOptions += '<option value="' + country.name + '">' + country.name + '</option>';
});
$('#country').html(countryOptions);
});
var country_name = $(this).val();
if (country_name != '') {
$.getJSON('/assets/json/countries.json', function (data) {
$.each(data, function (key, country) {
if (country_name == country.name) {
$("#code").text(country.dialCode);
}
});
});
}
if (country_name != 'Turkey') {
$('#idNumber').val('11111111111');
$('#idNumber').prop('disabled', true);
} else {
$('#idNumber').prop('disabled', false);
}
});
</script>
</html>
和控制器查看用户详情页面,
@Controller
@RequestMapping("/userdetails")
public class UserDetailsController {
@Autowired
private UserService userService;
@Resource
private MessageSource messageSource;
@GetMapping
public String userDetails(Model model){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
String email = principal.toString();
CCUser user = userService.findUserByEmail(email);
UserDto userDto = new UserDto();
BeanUtils.copyProperties(userDto, user);
model.addAttribute("userd", userDto);
return "userdetails";
}
}
主控,
@Controller
public class MainController {
@GetMapping("/home")
public String displayHomePage (Model model){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> grantedRoles = new ArrayList<GrantedAuthority>();
grantedRoles.addAll(auth.getAuthorities());
if(grantedRoles.stream().anyMatch(r -> r.getAuthority().equals("ROLE_USER"))){
return "uprojects";
}
return "home";
}
@GetMapping("/nfeatures")
public String displayNFeaturesPage (Model model){
return "nfeatures";
}
@GetMapping("/uprojects")
public String displayUProjectsPage (Model model){
return "uprojects";
}
@GetMapping("/pricing")
public String displayPricingPage (Model model){
return "pricing";
}
@GetMapping(value="/logout")
public String logoutPage (HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
new SecurityContextLogoutHandler().logout(request, response, auth);
}
return "redirect:/login?logout";//You can redirect wherever you want, but generally it's a good practice to show login screen again.
}
}