如何使用Spring Security + Angular进行登录/身份验证。

huangapple go评论89阅读模式
英文:

How to login/authenticate with Spring Security + Angular

问题

Sure, here's the translation of the provided content:

我正在尝试使用Spring Boot 2.3.1 + Spring Security + Angular 7创建一个Web应用程序。
目前,我的主要目标是,如果用户想要登录(使用自定义的Angular登录页面),前端会将数据(用户名和密码)发送到后端,然后我想要在后端进行身份验证并将消息发送回前端(例如:OK消息或其他消息)。

我的项目工作方式如下:
使用Maven构建Angular前端,并从“dist”文件夹中复制文件/文件夹并放入后端资源文件夹中。通过这种解决方案,一切都正常工作,现在我想添加Spring Security部分。

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
        .withUser("user1").password("{noop}password123").roles("USER");
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
        .csrf().disable()
        .authorizeRequests()
            //.antMatchers(HttpMethod.POST, "/auth").permitAll() // 我尝试过这个但不起作用
            .antMatchers(HttpMethod.GET, "/login", "/index*", "/static/**", "/*.js", "/*.json", "/*.ico", "/*.sccs", "/*.woff2", "/*.css").permitAll()
            .anyRequest().authenticated()
            .and()
          .formLogin()
            .loginPage("/")
            .loginProcessingUrl("/auth")
            .usernameParameter("username")
            .passwordParameter("password")
            .failureUrl("/index.html?error=true")
            .permitAll();
    }
}

ApiController.java

@RestController
public class ApiController {

    @PostMapping("/auth")
    public boolean login(@RequestBody User user) {
        return user.getUserName().equals("user") && user.getPassword().equals("password"); // 我想要使用auth.inMemoryAuthentication()进行身份验证
    }
}

我有一个User.java文件,包含两个变量(用户名和密码)。

login.components.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormGroup } from '@angular/forms';
import { LoggerService } from '@app/core/services/logger.service';
import { Http } from "@angular/http";

import { FormControlHelper, Globals } from '@app/core/helpers/index';
import { loginValidation } from '@app/models/form-validations/index';
import { HttpParams, HttpHeaders } from "@angular/common/http";
import { URLSearchParams } from "@angular/http";
import { MatSnackBar } from '@angular/material/snack-bar';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
})
export class LoginComponent implements OnInit {
    // 属性
    globals: Globals;
    public loginForm: FormGroup;
    public loginValidationModel: any;
    public waiting: boolean;
    public hidePassword: boolean;
    public params = new HttpParams();

    constructor(
        globals: Globals,
        private router: Router,
        private logger: LoggerService,
        public http: Http,
        private snackBar: MatSnackBar
    ) {
        this.loginValidationModel = loginValidation;
        this.hidePassword = true;
        this.globals = globals;

    }

    openSnackBar(message: string, action: string) {
        this.snackBar.open(message, action, {
          duration: 5000,
          verticalPosition: 'top', // 'top' | 'bottom'
          horizontalPosition: 'center', //'start' | 'center' | 'end' | 'left' | 'right'
          panelClass: ['red-snackbar'],
        });
      }

    ngOnInit() {
        const formGroupObj = FormControlHelper.generateFormControls(this.loginValidationModel);
        if (formGroupObj) {
            this.loginForm = new FormGroup(formGroupObj);
        } else {
            this.logger.error(new Error('Error generating the form modal & validations'));
        }

    }

    public onSubmit() {

       let urlSearchParams = new URLSearchParams();
       urlSearchParams.append('username', this.loginForm.value.username );
       urlSearchParams.append('password', this.loginForm.value.password );
       console.log("urlSearchParams: " + urlSearchParams);

       this.http.post("auth", urlSearchParams)
        .subscribe(
			    response => {
                    if(response) { // 在这里,我总是得到带有“OK”状态的200代码,即使用户名/密码不正确,我不知道如何修复这个部分
                        this.globals.loggeduser=this.loginForm.value.username;
                        this.router.navigateByUrl('/somewhere');
                    } else {
                        alert("Authentication failed");
                    }
			    }
			);

    } 
}

我尝试过参考https://spring.io/guides/tutorials/spring-security-and-angular-js/以及一些Baeldung教程来理解,但现在我有点困惑。有人能帮助我吗?谢谢,祝您愉快。

编辑:我构建了一个.war文件并使用Tomcat。

英文:

I am trying to do a webapp using Spring boot 2.3.1 + spring security + angular 7.
Right now, my main goal is if the user wants to log in (using the custom angular login page), the frontend sends the datas (username and password) to backend and there I want to authenticate and send back a message to frontend(like: OK message or something)

My project works like this:
With maven I build the angular frontend and from the "dist" folder I copy the files/folders and put into the backend resources folder. With this solution, everything is working, now I want to add the spring security part.

SecurityConfig.java

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password("{noop}password123").roles("USER");
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
//.antMatchers(HttpMethod.POST, "/auth").permitAll() // I tried this but nothing
.antMatchers(HttpMethod.GET, "/login", "/index*", "/static/**", "/*.js", "/*.json", "/*.ico", "/*.sccs", "/*.woff2", "/*.css").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.loginProcessingUrl("/auth")
.usernameParameter("username")
.passwordParameter("password")
.failureUrl("/index.html?error=true")
.permitAll();
}
}

ApiController.java

@RestController
public class ApiController {
@PostMapping("/auth")
public boolean login(@RequestBody User user) {
return user.getUserName().equals("user") && user.getPassword().equals("password"); // I would like to authenticate with auth.inMemoryAuthentication()
}
}

I have a User.java with 2 variables (username and password)

login.components.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormGroup } from '@angular/forms';
import { LoggerService } from '@app/core/services/logger.service';
import { Http } from "@angular/http";
import { FormControlHelper, Globals } from '@app/core/helpers/index';
import { loginValidation } from '@app/models/form-validations/index';
import { HttpParams, HttpHeaders} from "@angular/common/http";
import { URLSearchParams } from "@angular/http"
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
})
export class LoginComponent implements OnInit {
// Properties
globals: Globals;
public loginForm: FormGroup;
public loginValidationModel: any;
public waiting: boolean;
public hidePassword: boolean;
public params = new HttpParams();
constructor(
globals: Globals,
private router: Router,
private logger: LoggerService,
public http : Http,
private snackBar: MatSnackBar
) {
this.loginValidationModel = loginValidation;
this.hidePassword = true;
this.globals = globals;
}
openSnackBar(message: string, action: string) {
this.snackBar.open(message, action, {
duration: 5000,
verticalPosition: 'top', // 'top' | 'bottom'
horizontalPosition: 'center', //'start' | 'center' | 'end' | 'left' | 'right'
panelClass: ['red-snackbar'],
});
}
ngOnInit() {
const formGroupObj = FormControlHelper.generateFormControls(this.loginValidationModel);
if (formGroupObj) {
this.loginForm = new FormGroup(formGroupObj);
} else {
this.logger.error(new Error('Error generating the form modal & validations'));
}
}
public onSubmit() {
let urlSearchParams = new URLSearchParams();
urlSearchParams.append('username', this.loginForm.value.username );
urlSearchParams.append('password', this.loginForm.value.password );
console.log("urlSearchParams: " + urlSearchParams);
this.http.post("auth", urlSearchParams)
.subscribe(
response => {
if(response) { //Here I always get code 200 with "OK" status, even if the username/password is bad, I don't know how to fix this part
this.globals.loggeduser=this.loginForm.value.username;
this.router.navigateByUrl('/somewhere');
} else {
alert("Authentication failed");
}
}
);
} 
}

I tried this <https://spring.io/guides/tutorials/spring-security-and-angular-js/> to understand it (with some Baeldung tutorials), but I am a bit confused now. Can somebody help me? Thank you and have a nice day.

Edit: I build a .war file and using a Tomcat.

Edit2: More details and some progession. I show 2 examples, login with 1 valid and 1 invalid username/password. I can see that I get this "?error" part in the url, if the username/password is invalid. My problem: despite of the error, on the frontend side I am able to login and I can reach everything (subpages). How can I solve this issue? (if the login is valid -> redirect and authenticate on backend/frontend and store it, so I don't have to login on subpages ALSO if the login is invalid, don't redirect just stay on the login page with some error message) I know I have to rework the "public onSubmit()" method inside of login.component, but I am not sure how. Note: both response have "OK" true and status 200, which is not okay I guess

Edit3: I have 1 more question: If i am correct, with loginProcessingUrl I can't handle the datas with controller (mapping to /auth). So it is useless in this case, right?

如何使用Spring Security + Angular进行登录/身份验证。

如何使用Spring Security + Angular进行登录/身份验证。

答案1

得分: 2

我找到了一个可能的解决方案。

我稍微修改了SecurityConfiguration.java文件(添加了successHandler和failureHandler):

@Override
protected void configure(final HttpSecurity http) throws Exception {
    http.csrf().disable().authorizeRequests()
            .antMatchers(HttpMethod.GET, "/login", "/index*", "/static/**", "/*.js", "/*.json", "/*.ico", "/*.sccs","/*.woff2", "/*.css").permitAll()
            .anyRequest()
                .authenticated()
                .and()
            .formLogin()
                .loginPage("/")
                .loginProcessingUrl("/auth")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler(successHandler())
                .failureHandler(failureHandler())
                .permitAll()
                .and()
            .logout().permitAll();
}

private AuthenticationSuccessHandler successHandler() {
    return new AuthenticationSuccessHandler() {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                HttpServletResponse httpServletResponse, Authentication authentication)
                throws IOException, ServletException {
            httpServletResponse.getWriter().append("OK");
            httpServletResponse.setStatus(200);
        }
    };
}

private AuthenticationFailureHandler failureHandler() {
    return new AuthenticationFailureHandler() {
        @Override
        public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
                HttpServletResponse httpServletResponse, AuthenticationException e)
                throws IOException, ServletException {
            httpServletResponse.getWriter().append("Authentication failure");
            httpServletResponse.setStatus(401);
        }
    };
}

然后,我还更改了前端登录组件:

let urlSearchParams = new URLSearchParams();
urlSearchParams.append('username', this.loginForm.value.username );
urlSearchParams.append('password', this.loginForm.value.password );

if(this.loginForm.value.username != null && this.loginForm.value.password != null) {
    this.http.post("auth", urlSearchParams).subscribe(
        response => {
            if(response.status == 200 && response.ok == true) {
                this.globals.loggeduser=this.loginForm.value.username;
                this.router.navigateByUrl('/somewhere');
            } else {
                this.openSnackBar("Wrong username or password!","");
            }
        } 
    );
}

经过这些更改,一切都正常工作了。

英文:

I found a possible solution.

I reworked the SecurityConfiguration.java a little bit (added succesHandler/failureHandler)

SecurityConfiguration.java

@Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.GET, &quot;/login&quot;, &quot;/index*&quot;, &quot;/static/**&quot;, &quot;/*.js&quot;, &quot;/*.json&quot;, &quot;/*.ico&quot;, &quot;/*.sccs&quot;,&quot;/*.woff2&quot;, &quot;/*.css&quot;).permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage(&quot;/&quot;)
.loginProcessingUrl(&quot;/auth&quot;)
.usernameParameter(&quot;username&quot;)
.passwordParameter(&quot;password&quot;)
.successHandler(successHandler())
.failureHandler(failureHandler())
.permitAll()
.and()
.logout().permitAll();
}
private AuthenticationSuccessHandler successHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Authentication authentication)
throws IOException, ServletException {
httpServletResponse.getWriter().append(&quot;OK&quot;);
httpServletResponse.setStatus(200);
}
};
}
private AuthenticationFailureHandler failureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, AuthenticationException e)
throws IOException, ServletException {
httpServletResponse.getWriter().append(&quot;Authentication failure&quot;);
httpServletResponse.setStatus(401);
}
};
}

And after that I changed frontend login component too.

    let urlSearchParams = new URLSearchParams();
urlSearchParams.append(&#39;username&#39;, this.loginForm.value.username );
urlSearchParams.append(&#39;password&#39;, this.loginForm.value.password );
if(this.loginForm.value.username != null &amp;&amp; this.loginForm.value.password != null) {
this.http.post(&quot;auth&quot;, urlSearchParams).subscribe(
response =&gt; {
if(response.status == 200 &amp;&amp; response.ok == true) {
this.globals.loggeduser=this.loginForm.value.username;
this.router.navigateByUrl(&#39;/somewhere&#39;);
} else {
this.openSnackBar(&quot;Wrong username or password!&quot;,&quot;&quot;);
}
} 
);
}

And after these changes everything is working.

huangapple
  • 本文由 发表于 2020年7月28日 05:16:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63123694.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定