首页 > 解决方案 > Spring Boot / Thymeleaf - Javascript 请求和 X.509 Auth 的身份验证困难

问题描述

我有一个配置为使用 .x509 证书身份验证的 Spring Boot Web 应用程序。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests().antMatchers("/h2-console/**", "/css/**", "/image/**", "/js/**").permitAll()
                .and().authorizeRequests().antMatchers("/**").authenticated().and().x509()
                .userDetailsService(userDetailsService).subjectPrincipalRegex("CN=(.*?),");

    }
}

用户详情服务:

@Service
public class AppUserDetailsService implements UserDetailsService {

    @Autowired
    private AppUserRepo userRepo;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        var userVal = userRepo.getUser(username);
        if (!userVal.isPresent()) {
            throw new UsernameNotFoundException("No user with name: " + username);
        }
        var user = userVal.get();
        return new User(user.getUsername(), "",
                user.getRoles().stream().map(r -> new SimpleGrantedAuthority(r)).collect(Collectors.toList()));
    }

}

我的应用程序大部分时间都在工作;也就是说,我可以使用此设置访问需要授权的完整页面。当我尝试使用 JavaScript/TypeScript 加载片段时,就会出现问题。当我单击触发 JS 调用的按钮时,我收到以下错误:

{"timestamp":"2019-09-17T14:08:44.019+0000","status":403,"error":"Forbidden","message":"Forbidden","path":"/employee/search"}

JS/TS 片段

function loadEmployees() {

    const container = document.getElementById("resultPanel");
    const url = "employee/search";
    fetch(url, {
        method: "POST",
        headers: {
            'Content-Type': 'application/json',

        },
        body: JSON.stringify(buildBody())
    })
        .then(res => res.text())
        .then(res => container.innerHTML = res);

}

插入片段的模板:

<div id="page" class="container">

    <h1>Employee Search</h1>
    <div class="clearfix">
        <div class='form-row my-3'>
            <label for="reqType">Search Type: </label>
            <select class="form-control" name="reqType" id="reqType">
                <option>Select</option>
                <option value=1>SSN</option>
                <option value=2>Name</option>
            </select>
        </div>
     /// Code omitted for brevity
        <button class="btn btn-primary float-right" id="searchBtn">Search</button>
    </div>

片段本身:

<div th:fragment="results-list">
    <div class="row d-md-flex flex-header d-none">

        <div class="col-md-4">
            Name
        </div>
        <div class="col-md-2">
            Loc.
        </div>
        <div class="col-md-2">
            DB
        </div>
        <div class="col-md-1">

        </div>
    </div>
    <div class="row d-flex" th:each="emp : ${employees}">

        <div class="col-md-4">
            <span class="font-weight-bold d-inline d-md-none pr-1">Name: </span>
            <span th:text="${emp.lastName} + ', ' + ${emp.firstName} + ' ' + ${emp.middleName}"></span>
        </div>
        <div class=" col-md-2">
            <span class="font-weight-bold d-inline d-md-none pr-1">Loc.: </span>
            <span th:text="${emp.location}"></span>
        </div>
        <div class="col-md-2">
            <span class="font-weight-bold d-inline d-md-none pr-1">DB: </span>
            <span th:text="${emp.dbName}"></span>
        </div>
        <div class="col-md-1">
            <span class="font-weight-bold d-inline d-md-none pr-1">Edit: </span>
            <a th:href="@{'/employee/' + ${emp.id}}">View <i class="fas fa-edit"></i></a>

        </div>
    </div>
</div>

    <hr />
    <h2 class="mt-1">Results</h2>
    <div id="resultPanel" class="mt-4">

    </div>
</div>

..以及加载片段的控制器:

@Controller
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private EmployeeService empService;

    @PostMapping("/search")
    public String searchByName(@RequestBody SearchDTO dto, Model model) {
        model.addAttribute("employees", empService.searchEmployees(dto));
        return "fragments/results-list";
    }

请注意,我意识到 JavaScript 和 Java 是非常不同的语言;鉴于我不确定是否必须在客户端或服务器端执行更改,因此我将两个标签都包括在内。如果您认为这样不合适,请随意删除一个。

非常感谢。

标签: javascriptjavaspringspring-boot

解决方案


我遇到了同样的问题,发现我需要在我的开放资源之前指定我的安全资源。

因此,您可能会在以下方面获得更好的运气:

httpSecurity
    .csrf().disable()   
    .authorizeRequests().antMatchers("/**").authenticated().and().x509()
    .userDetailsService(userDetailsService).subjectPrincipalRegex("CN=(.*?),")
    .and()
    .authorizeRequests().antMatchers("/h2-console/**", "/css/**", "/image/**", "/js/**").permitAll()
    ;

请参阅此处的文档

根据反馈进行编辑,使其成为编译器友好的代码示例。

编辑 #2 - 添加 .csrf().disable()。我必须自己这样做才能让 REST 工作,并且看到其他人也这样做。


推荐阅读