英文:
problems using Roles and Permissions using Spring security
问题
我正在使用Spring Security 3。我的目标是根据角色和权限为特定用户授予特定操作(控制器方法)。
ROLE_ADMIN -> 创建、读取、更新和删除
ROLE_USER -> 读取和更新
当我注册一个用户时,它会以加密密码的用户对象保存到数据库中,没有问题。但是当我使用Postman使用基本身份验证命中控制器API时,它显示状态码200 OK,但不是预期的JSON输出。然后,在遇到这个问题后,当我使用Google Chrome访问相同的URL时,它显示状态码404 NOT FOUND的白标签错误页面。
这是我的SecurityConfig.java文件:
@Configuration
//@Import({GsonConfig.class})
@EnableMethodSecurity
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Autowired
ProductUserRepository productuserrepo;
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return username -> productuserrepo.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(csrf -> csrf.disable())
.cors(cors -> cors.disable())
.authorizeHttpRequests(a -> {
a.requestMatchers("/auth/**").permitAll();
a.anyRequest().authenticated();
})
.formLogin(login -> {
login.loginProcessingUrl("/login")
.failureForwardUrl("/loginfailure");
})
.logout(logout ->{
logout.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(false)
.logoutSuccessUrl("/successlogout")
.logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext());
})
.authenticationManager(authManager())
.build();
}
@Bean
public AuthenticationManager authManager(){
DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
daoProvider.setUserDetailsService(userDetailsService());
daoProvider.setPasswordEncoder(encoder());
return new ProviderManager(daoProvider);
}
}
这是我的Role.java文件(使用List
@RequiredArgsConstructor
public enum Role {
USER(Set.of(USER_READ,USER_UPDATE)),
ADMIN(Set.of(ADMIN_READ,ADMIN_UPDATE,ADMIN_DELETE,ADMIN_CREATE,
USER_READ,USER_UPDATE));
@Getter
private final Set<Permission> permissions;
public List<SimpleGrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = getPermissions().stream()
.map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
.collect(Collectors.toList());
authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
return authorities;
}
}
这是我的Permission.java文件:
public enum Permission {
ADMIN_READ("admin:read"),
ADMIN_UPDATE("admin:update"),
ADMIN_CREATE("admin:create"),
ADMIN_DELETE("admin:delete"),
USER_READ("user:read"),
USER_UPDATE("user:update");
@Getter
private final String permission;
}
最后,这是我的UserController用于读取资源的代码:
@RestController
@RequestMapping("/user")
@PreAuthorize("hasAnyRole('USER','ADMIN')")
public class UserController {
@PreAuthorize("hasAnyAuthority('admin:read','user:read')")
@GetMapping(value = "/findproduct",produces = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json",consumes = MediaType.APPLICATION_JSON_VALUE)
public Product findproductbyid(@RequestParam("productid")int productid) {
System.out.println("inside findproduct method");
return prodrepo.findById(productid).get();
}
}
还有我的AuthController用于注册:
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
ProductUserRepository produserrepo;
@Autowired
PasswordEncoder pwd_encoder;
@Autowired
Gson gson;
@PostMapping(value = "/register",produces = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json",consumes = MediaType.APPLICATION_JSON_VALUE)
public String register(@RequestBody ProductUserModel user) {
if (produserrepo.existsByUsername(user.getUsername())) {
return user.getUsername() + " already taken";
}else {
System.out.println(user.toString());
user.setPassword(pwd_encoder.encode(user.getPassword()));
produserrepo.save(gson.fromJson(gson.toJson(user), ProductUser.class));
return user.getUsername() + " succesfully registered";
}
}
我的唯一问题是:当我使用Google Chrome访问http://localhost:8081/user/findproduct?productid=1
时,会重定向到登录页面。然后,如果我使用我成功注册并保存到数据库的用户名/密码凭据,它会显示状态码404 NOT FOUND的白标签错误页面。
有人有任何解决此问题的想法吗?
英文:
I'm using Spring Security 3.
My objective is to grant specific operations(controller methods) based on roles and permissions with a particular user.
ROLE_ADMIN -> create,read,update and delete
ROLE_USER -> read and update.
When I register a user, it is saved as a user object to a database with encrypted password with no issue. But when I hit the controller api using UserController
with basic authentication using Postman, it is showing status 200 OK but not the expected json output. Then after facing that when I hit the same url with Google Chrome, it is showing Whitelabel error page with status 404 NOT FOUND.
Here is my SecurityConfig.java
@Configuration
//@Import({GsonConfig.class})
@EnableMethodSecurity
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Autowired
ProductUserRepository productuserrepo;
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Bean
public UserDetailsService userDetailsService() {
return username -> productuserrepo.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(csrf -> csrf.disable())
.cors(cors -> cors.disable())
.authorizeHttpRequests(a -> {
a.requestMatchers("/auth/**").permitAll();
a.anyRequest().authenticated();
})
.formLogin(login -> {
login.loginProcessingUrl("/login")
.failureForwardUrl("/loginfailure");
})
.logout(logout ->{
logout.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(false)
.logoutSuccessUrl("/successlogout")
.logoutSuccessHandler((request, response, authentication) -> SecurityContextHolder.clearContext());
})
.authenticationManager(authManager())
.build();
}
@Bean
public AuthenticationManager authManager(){
DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
daoProvider.setUserDetailsService(userDetailsService());
daoProvider.setPasswordEncoder(encoder());
return new ProviderManager(daoProvider);
}
}
Here is my Role.java [getting list of permissions using List<SimpleGrantedAuthority>
]
@RequiredArgsConstructor
public enum Role {
USER(Set.of(USER_READ,USER_UPDATE)),
ADMIN(Set.of(ADMIN_READ,ADMIN_UPDATE,ADMIN_DELETE,ADMIN_CREATE,
USER_READ,USER_UPDATE));
@Getter
private final Set<Permission> permissions;
public List<SimpleGrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = getPermissions().stream()
.map(permission -> new SimpleGrantedAuthority(permission.getPermission()))
.collect(Collectors.toList());
authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name()));
return authorities;
}
}
Here is my Permission.java
public enum Permission {
ADMIN_READ("admin:read"),
ADMIN_UPDATE("admin:update"),
ADMIN_CREATE("admin:create"),
ADMIN_DELETE("admin:delete"),
USER_READ("user:read"),
USER_UPDATE("user:update");
@Getter
private final String permission;
}
Finally here is my user controller to read resource
@RestController
@RequestMapping("/user")
@PreAuthorize("hasAnyRole('USER','ADMIN')")
public class UserController {
@PreAuthorize("hasAnyAuthority('admin:read','user:read')")
@GetMapping(value = "/findproduct",produces = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json",consumes = MediaType.APPLICATION_JSON_VALUE)
public Product findproductbyid(@RequestParam("productid")int productid) {
System.out.println("inside findproduct method");
return prodrepo.findById(productid).get();
}
}
And my AuthController to register :
@RestController
@RequestMapping("/auth")
public class AuthController {
@Autowired
ProductUserRepository produserrepo;
@Autowired
PasswordEncoder pwd_encoder;
@Autowired
Gson gson;
@PostMapping(value = "/register",produces = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json",consumes = MediaType.APPLICATION_JSON_VALUE)
public String register(@RequestBody ProductUserModel user) {
if (produserrepo.existsByUsername(user.getUsername())) {
return user.getUsername() + " already taken";
}else {
System.out.println(user.toString());
user.setPassword(pwd_encoder.encode(user.getPassword()));
produserrepo.save(gson.fromJson(gson.toJson(user), ProductUser.class));
return user.getUsername() + " succesfully registered";
}
}
My only issue is :
when I hit http://localhost:8081/user/findproduct?productid=1
for UserController
using Google Chrome, then login page redirected. Then if I hit username/password credentials which I successfully registered and saved to database, it is showing Whitelabel error page with status 404 NOT FOUND.
Can anyone has any idea to fix the issue?
答案1
得分: 2
好的,以下是翻译好的内容:
好的,我自己解决了。
首先,我在AuthController中添加了登录方法:
@GetMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json", consumes = MediaType.APPLICATION_JSON_VALUE)
public void login(HttpServletRequest req, @RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) {
if (authorization != null && authorization.toLowerCase().startsWith("basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
String credentials = new String(credDecoded, StandardCharsets.UTF_8);
// credentials = username:password
final String[] values = credentials.split(":", 2);
String username = values[0];
String password = values[1];
authenticateusercredentials(username, password, req);
} else {
String username = req.getParameter("username");
String password = req.getParameter("password");
authenticateusercredentials(username, password, req);
}
}
public void authenticateusercredentials(String username, String password, HttpServletRequest req) {
UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authmanager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
HttpSession session = req.getSession(true);
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);
}
其次,我在Security Configuration类中将/login更改为/auth/login:
...
.formLogin(login -> {
login.loginProcessingUrl("/auth/login")
.failureForwardUrl("/loginfailure");
})
...
谢谢和祝好!
<details>
<summary>英文:</summary>
OK I resolved by myself.
Firstly, I add login method inside AuthController
@GetMapping(value = "/login",produces = MediaType.APPLICATION_JSON_VALUE,
headers = "Accept=application/json",consumes = MediaType.APPLICATION_JSON_VALUE)
public void login(HttpServletRequest req,@RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) {
if (authorization != null && authorization.toLowerCase().startsWith("basic")) {
// Authorization: Basic base64credentials
String base64Credentials = authorization.substring("Basic".length()).trim();
byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
String credentials = new String(credDecoded, StandardCharsets.UTF_8);
// credentials = username:password
final String[] values = credentials.split(":", 2);
String username = values[0];
String password = values[1];
authenticateusercredentials(username, password, req);
}else {
String username = req.getParameter("username");
String password = req.getParameter("password");
authenticateusercredentials(username, password, req);
}
}
public void authenticateusercredentials(String username,String password,HttpServletRequest req) {
UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authmanager.authenticate(authReq);
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
HttpSession session = req.getSession(true);
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, sc);
}
Secondly I changed /login to auth/login in Security Configuration class
...
.formLogin(login -> {
login.loginProcessingUrl("/auth/login")
.failureForwardUrl("/loginfailure");
})
...
Thanks and Regards
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论