问题描述
我是 Spring Boot 的初学者,并且真的很难做到这一点。我根本没有发现 Spring boot 的安全 API 非常直观,但我正在尝试获取它。
我将使用 MysqL 和 JPA 从数据库中获取用户, 但最初只是为了确保安全脚手架正常工作,我只是硬编码来自员工详细信息服务的用户。
经过很多麻烦,我已经设法使基本身份验证工作,但授权不起作用。 我已经编辑了问题以反映基本问题:
我知道在人们将其标记为重复之前还有其他问题,我已经完成了所有这些问题,他们都回答了我认为已经在我的应用中使用的基本答案。
最后 /home 路径用于 home 方法, /mama 路径用于角色 mama
代码如下:
//AppSecurityConfig.java
package com.straightwalls.absencemanagement.config;
import com.straightwalls.absencemanagement.service.EmployeeDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config
.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import
org.springframework.security.config
.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
protected void configure(HttpSecurity http) throws Exception {
//disabled for development
http.authorizeRequests()
.antMatchers("/mama").hasRole("MAMA")
.antMatchers("/home").hasAnyRole("ROLE_HEAD","ROLE_MAMA")
.and()
.formLogin();
}
@Bean
/*
* Returning no op password encoder for Now,* as we are not encoding passwords as no registration
* implemented for Prototype. We would need to add the users from a separate service. W
*
* */
public PasswordEncoder getpasswordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
//EmployeeDetailsService
package com.straightwalls.absencemanagement.service;
import com.straightwalls.absencemanagement.model.Employee;
import com.straightwalls.absencemanagement.model.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@Service
public class EmployeeDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
if (!username.equals("Mama")){
throw new UsernameNotFoundException(
"You got the wrong Username,should be mama"
);
}
Employee employee = new Employee();
Role role = new Role();
role.setName("HEAD");
employee
.setUsername(username)
.setPassword("1234")
.setRole(role);
return new EmployeePrincipal(employee);
}
}
//EmployeePrincipal.java
package com.straightwalls.absencemanagement.service;
import com.straightwalls.absencemanagement.model.Employee;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Arrays;
import java.util.Collection;
public class EmployeePrincipal implements UserDetails {
private Employee employee;
//Added another default Constructor,incase
public EmployeePrincipal(){
}
public EmployeePrincipal(Employee employee){
this.employee = employee;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_HEAD"));
//return Arrays.asList(new SimpleGrantedAuthority("ROLE_" + employee.getRole().getName()));
}
@Override
public String getpassword() {
return employee.getpassword();
}
@Override
public String getUsername() {
return employee.getUsername();
}
/*
* Methods below are the rubbish methods,we keep as true for Now
*
* */
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
//Login.java
package com.straightwalls.absencemanagement.api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginApi {
@RequestMapping("/")
public String index(){
return "Straight Walls absence management!";
}
@RequestMapping("/home")
public String home(){
return "Welcome to Home!";
}
/**
* This method can be deleted in the end
*
*/
@RequestMapping("/mama")
public String roletest(){
return "This end point is only for Mama!";
}
}
//Employee.java
package com.straightwalls.absencemanagement.model;
import org.hibernate.annotations.Fetch;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Entity
public class Employee {
@Id
private long id;
private String firstName;
private String lastName;
private String username;
private String password;
private String startDate;
@ManyToOne
@JoinColumn(name = "roleId",referencedColumnName = "id")
private Role role;
@ManyToOne
@JoinColumn(name = "departmentId",referencedColumnName = "id")
private Department department;
public Employee(){
}
public long getId() {
return id;
}
public Employee setId(long id) {
this.id = id;
return this;
}
public String getFirstName() {
return firstName;
}
public Employee setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public String getLastName() {
return lastName;
}
public Employee setLastName(String lastName) {
this.lastName = lastName;
return this;
}
public String getUsername() {
return username;
}
public Employee setUsername(String username) {
this.username = username;
return this;
}
public String getpassword() {
return password;
}
public Employee setPassword(String password) {
this.password = password;
return this;
}
public String getStartDate() {
return startDate;
}
public Employee setStartDate(LocalDate startDate) {
this.startDate =
startDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
return this;
}
public Role getRole() {
return role;
}
public Employee setRole(Role role) {
this.role = role;
return this;
}
public Department getDepartment() {
return department;
}
public Employee setDepartment(Department department) {
this.department = department;
return this;
}
}
服务器日志: 2021-04-21 07:42:12.151 INFO 10370 --- [restartedMain] traightWallsAbsenceManagementApplication :在 Peshotans-MacBook-Air.local 上使用 Java 16 启动 StraightWallsAbsenceManagementApplication,PID 为 10370 (/Users/pallagesAbpeightIdean由 peshotanpavri 在 /Users/peshotanpavri/java/IdeaProjects/StraightWallsAbsenceManagement 中启动的类) 2021-04-21 07:42:12.154 INFO 10370 --- [restartedMain] traightWallsAbsenceManagementApplication:没有设置活动配置文件,回退到默认配置文件:默认 2021-04-21 07:42:12.254 INFO 10370 --- [restartedMain].e.DevToolsPropertyDefaultsPostProcessor:Devtools 属性默认激活!将 'spring.devtools.add-properties' 设置为 'false' 以禁用 2021-04-21 07:42:12.255 INFO 10370 --- [restartedMain] .e.DevToolsPropertyDefaultsPostProcessor:对于其他与 Web 相关的日志记录,请考虑将“logging.level.web”属性设置为“DEBUG” 2021-04-21 07:42:14.065 INFO 10370 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate :在默认模式下引导 Spring Data JPA 存储库。 2021-04-21 07:42:14.203 INFO 10370 --- [restartedMain] .s.d.r.c.RepositoryConfigurationDelegate:在 113 毫秒内完成 Spring 数据存储库扫描。找到 1 个 JPA 存储库接口。 2021-04-21 07:42:15.235 INFO 10370 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcat 初始化端口:8080 (http) 2021-04-21 07:42:15.253 INFO 10370 --- [restartedMain] o.apache.catalina.core.StandardService:启动服务 [Tomcat] 2021-04-21 07:42:15.253 INFO 10370 --- [restartedMain] org.apache.catalina.core.StandardEngine:启动 Servlet 引擎:[Apache Tomcat/9.0.45] 2021-04-21 07:42:15.404 INFO 10370 --- [restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : 初始化 Spring 嵌入式 WebApplicationContext 2021-04-21 07:42:15.405 INFO 10370 --- [restartedMain] w.s.c.ServletWebServerApplicationContext:根 WebApplicationContext:初始化在 3147 毫秒内完成 2021-04-21 07:42:15.642 INFO 10370 --- [restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - 开始... 2021-04-21 07:42:16.670 INFO 10370 --- [restartedMain] com.zaxxer.hikari.HikariDataSource:HikariPool-1 - 启动完成。 2021-04-21 07:42:16.743 INFO 10370 --- [restartedMain] o.hibernate.jpa.internal.util.LogHelper:HHH000204:处理 PersistenceUnitInfo [名称:默认] 2021-04-21 07:42:16.859 INFO 10370 --- [restartedMain] org.hibernate.Version:HHH000412:Hibernate ORM 核心版本 5.4.30.Final 2021-04-21 07:42:17.121 INFO 10370 --- [restartedMain] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.2.Final} 2021-04-21 07:42:17.327 INFO 10370 --- [restartedMain] org.hibernate.dialect.Dialect:HHH000400:使用方言:org.hibernate.dialect.MysqL8Dialect 2021-04-21 07:42:18.359 INFO 10370 --- [restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator:HHH000490:使用JtaPlatform实现:[org.hibernate.engine.transaction.jta.Jta.No. 2021-04-21 07:42:18.383 INFO 10370 --- [restartedMain] j.LocalContainerEntityManagerfactorybean:为持久性单元“默认”初始化 JPA EntityManagerFactory 2021-04-21 07:42:18.458 WARN 10370 --- [restartedMain] JpaBaseConfiguration$JpaWebConfiguration:spring.jpa.open-in-view 默认启用。因此,可以在视图呈现期间执行数据库查询。显式配置 spring.jpa.open-in-view 禁用此警告 2021-04-21 07:42:18.737 INFO 10370 --- [restartedMain] ossweb.DefaultSecurityFilterChain :将使用 [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7e4d562b,org.springframework 保护任何请求.security.web.context.SecurityContextPersistenceFilter@657e3908,org.springframework.security.web.header.HeaderWriterFilter@484dbda9,org.springframework.security.web.csrf.CsrfFilter@358c0253,org.springframework.security.web.outthentication .logoutFilter@4596a3b,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6dceb9b9,org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@697f408b,org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@6dceb9b9.,org.springframework.security.web.authentication.www.BasicAuthenticationFilter@166853dc,org.springframework.security.web.savedrequest.RequestCacheAwareFilter@185f798b,org.springframework.security.w eb.servletapi.SecurityContextHolderAwareRequestFilter@7f7692e4,org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5406fd30,org.springframework.security.web.session.SessionManagementFilter@66387082,org.springframework.security.web.Filter@4fd. org.springframework.security.web.access.intercept.FilterSecurityInterceptor@768269d7] 2021-04-21 07:42:18.981 信息 10370 --- [restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor:初始化 ExecutorService 'applicationTaskExecutor' 2021-04-21 07:42:19.784 INFO 10370 --- [restartedMain] o.s.b.d.a.OptionalLiveReloadServer:LiveReload 服务器正在端口 35729 上运行 2021-04-21 07:42:19.835 INFO 10370 --- [restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer:Tomcat 在端口上启动:8080 (http),上下文路径为“” 2021-04-21 07:42:19.851 INFO 10370 --- [restartedMain] traightWallsAbsenceManagementApplication:在 8.486 秒内启动 StraightWallsAbsenceManagementApplication(JVM 运行 10.642) 2021-04-21 07:42:45.273 INFO 10370 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : 初始化 Spring dispatcherServlet 'dispatcherServlet' 2021-04-21 07:42:45.275 INFO 10370 --- [nio-8080-exec-1] o.s.web.servlet.dispatcherServlet:初始化 Servlet 'dispatcherServlet' 2021-04-21 07:42:45.300 INFO 10370 --- [nio-8080-exec-1] o.s.web.servlet.dispatcherServlet:在 24 毫秒内完成初始化
我使用虚拟角色 HEAD 登录。 所以我应该能够访问:/,/home 但不能访问 /mama 我无法访问 /home,即使角色已正确定义
在我的基础包中,我有以下包: config:有了这个配置文件, 模型:所有实体 api:控制器 存储库:存储库(尚未使用) 服务:我有 EmployeeDetailsService 和 EmployeeDetails 类
任何建议都将不胜感激,因为我希望它能够工作,但它只是不会抛出任何错误并且只是不断说错误的凭据,即使我已经输入了用户名 Mama 和密码 Mama 并尝试使用 NoOpPasswordEncoder
解决方法
通过清理代码并对不需要 ROLE 的 grantAuthorities 方法进行了一些更改,设法解决了这个问题_
还通过消除 ROLE_ 用 hasAuthority 和 hasAnyAuthority 更改了 hasRole、hasAnyRole_ 不知道为什么,但现在可以用了。
而且 API 现在更容易理解了
这是更新的代码:
@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
/*auth.inMemoryAuthentication()
.withUser("appuser").password("1234").roles("HEAD")
.and()
.withUser("Mama").password("Mama").roles("MAMA");*/
}
@Override
/*
* Now we have learnt the basics of Spring Security & Authrization method is completed.
* Lets fix Authentication first!
* Got it to work with hasAuthority & hasAnyAuthority but not with roles,not sure why,but it works atm
*
* */
protected void configure(HttpSecurity http) throws Exception {
//Disabled for development
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/mama").hasAuthority("MAMA")
.antMatchers("/home").hasAnyAuthority("HEAD","MAMA")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/")
.defaultSuccessUrl("/home");
}
@Bean
/*
* Returning no op password encoder for now,as we are not encoding passwords as no registration
* implemented for Prototype. We would need to add the users from a separate service. W
*
* */
public PasswordEncoder getPasswordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
//EmployeeDetailsService.java
@Service
public class EmployeeDetailsService implements UserDetailsService {
@Override
/*
* First,we are testing the Employee details service,independent of the Database,just to make sure we have this part working,* For the purpose of these prototypes,we wont use password encoder because we are not registering,*
* */
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (!username.equals("Mama")){
throw new UsernameNotFoundException("You got the wrong Username,should be mama");
}
Employee employee = new Employee();
Role role = new Role();
role.setName("HEAD");
employee
.setUsername(username)
.setPassword("1234")
.setRole(role);
return new EmployeePrincipal(employee);
}
}
//EmployeePrincipal.java
public class EmployeePrincipal implements UserDetails {
private Employee employee;
public EmployeePrincipal(Employee employee){
this.employee = employee;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(employee.getRole().getName()));
return authorities;
}
@Override
public String getPassword() {
return employee.getPassword();
}
@Override
public String getUsername() {
return employee.getUsername();
}
/*
* Methods below are the rubbish methods,we keep as true for now
*
* */
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
@RestController
public class LoginApi {
@RequestMapping("/")
public String index(){
return "Index"
}
@RequestMapping("/home")
public String home(){
return "Home!";
}
/*
* This method can be deleted in the end
* */
@RequestMapping("/mama")
public String roleTest(){
return "This end point is only for Mama!";
}
}