I have a JWT where the roles can be found under a specific claim. This claim is in a nested structure.
How can I tell the JwtAuthenticationConverter to find the roles under a certain path ?
As Authorization-Server I use Keycloak. It would be a possibility to add a mapper for the roles. But I want to exclude this possibility for now, because the goal is to find the roles under the specific claim.
Here is my decoded JWT. The role "user-role" should be located under the claim: "resource_access" -> "user" -> "roles":
"resource_access": {
"admin": {
"roles": [
"admin-role"
]
},
"user": {
"roles": [
"user-role"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
And here is my configuration for the JwtAuthenticationConverter:
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Override
public void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter());
}
private static JwtAuthenticationConverter jwtAuthenticationConverter()
{
var jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("resource_access.user.roles");
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
var jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}
I hope someone can help me. Thanks :)
thanks for your help. The solution was to implement a custom converter.
Here is my solution:
My custom converter:
#AllArgsConstructor
public class KeycloakJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken>
{
#NotEmpty
private List<String> clientIds;
#Override
public AbstractAuthenticationToken convert(Jwt source)
{
return new JwtAuthenticationToken(source, Stream.concat(new JwtGrantedAuthoritiesConverter().convert(source)
.stream(), extractResourceRoles(source).stream())
.collect(toSet()));
}
private Collection<? extends GrantedAuthority> extractResourceRoles(Jwt jwt)
{
var resourceAccess = new HashMap<>(jwt.getClaim("resource_access"));
var resourceRoles = new ArrayList<>();
clientIds.stream().forEach(id ->
{
if (resourceAccess.containsKey(id))
{
var resource = (Map<String, List<String>>) resourceAccess.get(id);
resource.get("roles").forEach(role -> resourceRoles.add(id + "_" + role));
}
});
return resourceRoles.isEmpty() ? emptySet() : resourceRoles.stream().map(r -> new SimpleGrantedAuthority("ROLE_" + r)).collect(toSet());
}
}
My security config:
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Value("${spring.security.jwt.role.locations}")
private List<String> rolesLocation;
#Override
public void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new KeycloakJwtAuthenticationConverter(rolesLocation));
}
}
Related
Here is the configuration class where I distribute access according to roles:
#Configuration
#EnableWebSecurity
#AllArgsConstructor
//#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
private final UserDetailServiceImpl userDetailsService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private static final String[] PUBLIC_URLS = {
"/v2/api-docs",
"/swagger-resources/**",
"/swagger-ui/**",
"/webjars/**"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.antMatchers(PUBLIC_URLS).permitAll()
.antMatchers ("/user/**"). hasAnyRole ("USER", "ADMIN")
.antMatchers("/user/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
/* this disables session creation on Spring Security */
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
This is the controller class where I want to use the admin role:
#AllArgsConstructor
#Validated
#RestController
#RequestMapping("/user")
#Api( tags = "Users")
public class UserController {
private final UserService userService;
#DeleteMapping("/admin/{id}")
#PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> delete(#PathVariable #Min(1) int id) {
userService.delete(id);
return ResponseEntity.ok().build();
}
If you delete in the configuration:
.antMatchers ("/user/**"). hasAnyRole ("USER", "ADMIN")
.antMatchers("/user/admin/**").hasRole("ADMIN")
Then the USER will also delete it. But if you leave this matching of roles, and make a request from the admin, then an error occurs when deleting the user.
{
"timestamp": "2021-12-01T21:30:05.647+00:00",
"status": 403,
"error": "Forbidden",
"message": "",
"path": "/user/admin/2"
}
If you leave this annotation #EnableGlobalMethodSecurity(prePostEnabled = true), then again the same error and nothing can be deleted.
Are there any suggestions to solve this problem?
I have a spring boot app that uses JWT for all endpoints. Now I want to add an /actuator endpoint that uses basic auth to enable prometheus scraping metrics.
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
class SecurityConfig(
val userService: UserService
) {
#Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
return http {
csrf { disable() }
formLogin { disable() }
httpBasic { disable() }
authorizeExchange {
authorize(ServerWebExchangeMatchers.pathMatchers(HttpMethod.OPTIONS, "/**"), permitAll)
// the following should not use JWT but basic auth
authorize(ServerWebExchangeMatchers.pathMatchers("/actuator"), authenticated)
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
jwt {
jwtAuthenticationConverter = customConverter()
}
}
}
}
}
In the MVC Stack I would use something like this:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Configuration
#Order(1)
public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Value("${management.endpoints.web.base-path}")
private String managementPath;
#Value("${config.actuator.user.name}")
private String actuatorUser;
#Value("${config.actuator.user.password}")
private String actuatorPassword;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(actuatorUser)
.password(passwordEncoder().encode(actuatorPassword))
.authorities("ROLE_ACTUATOR");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new Argon2PasswordEncoder();
}
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher(managementPath + "/**")
.cors().and()
.csrf().disable()
.authorizeRequests()
.anyRequest()
.hasRole("ACTUATOR")
.and()
.httpBasic();
}
}
#Configuration
#Order(2)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authenticationProvider(...)
.authorizeRequests()
// ...
}
}
}
How does this translate to webflux?
Adding multiple beans is possible but requires using securityMatcher:
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
class SecurityConfig(
val userService: UserService
) {
#Value("\${config.actuator.user.name}")
private val actuatorUser: String? = null
#Value("\${config.actuator.user.password}")
private val actuatorPassword: String? = null
#Bean
#Order(1)
fun springSecurityFilterChainActuator(http: ServerHttpSecurity): SecurityWebFilterChain? {
val encoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
return http {
securityMatcher(ServerWebExchangeMatchers.pathMatchers("/actuator/**"))
csrf { disable() }
formLogin { disable() }
httpBasic {
authenticationManager = UserDetailsRepositoryReactiveAuthenticationManager(
MapReactiveUserDetailsService(
User
.withUsername(actuatorUser)
.password(encoder.encode(actuatorPassword))
.roles("ACTUATOR")
.build()
)
)
}
authorizeExchange {
authorize(anyExchange, hasRole("ACTUATOR"))
}
}
}
#Bean
#Order(2)
fun springSecurityFilterChainApi(http: ServerHttpSecurity): SecurityWebFilterChain? {
return http {
securityMatcher(ServerWebExchangeMatchers.pathMatchers("/**"))
csrf { disable() }
formLogin { disable() }
httpBasic { disable() }
authorizeExchange {
authorize(ServerWebExchangeMatchers.pathMatchers(HttpMethod.OPTIONS, "/**"), permitAll)
authorize(anyExchange, authenticated)
}
oauth2ResourceServer {
jwt {
jwtAuthenticationConverter = customConverter()
}
}
}
}
}
I get all active users, but when the user logs out, it is still listed as the active user. How do I prevent a user from being listed as an active user after logging out?
I have not been able to find solutions in the documentation.
https://github.com/romanych2021/TestSession
Thanks.
ActiveUserServiceImpl.java
#Service
public class ActiveUserServiceImpl implements ActiveUserService{
#Autowired
SessionRegistry sessionRegistry;
public List<String > getAllActiveUser(){
List<Object> principals = sessionRegistry.getAllPrincipals();
User[] users = (User[]) principals.toArray(new User[0]);
return Arrays.stream(users)
.filter(user -> !sessionRegistry.getAllSessions(user, false)
.isEmpty()).map(User::getUsername).collect(Collectors.toList());
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/").permitAll()
.mvcMatchers("/login").anonymous()
.mvcMatchers("/user", "/allUser").hasAnyRole("ADMIN", "USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.and().csrf().disable()
.logout()
.permitAll()
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
.and().sessionManagement()
.maximumSessions(1)
.expiredUrl("/login")
.sessionRegistry(sessionRegistry);
}
}
HTML
<form method="post" action="/logout">
<button type="submit">Exit</button>
</form>
keep your active users map in a hashMap and remove the loooged out users from that map when day log out.In the value unbound event you can exclude inactive users.
#Getter
#Setter
public class ActiveUserStore {
public HashMap<String, ActorUser> userDetails;
public ActiveUserStore() {
userDetails = new HashMap<>();
}
}
#Getter
#Setter
public class LoggedUser implements HttpSessionBindingListener {
private ActorUser sessionUser;
private ActiveUserStore activeUserStore;
public LoggedUser(ActorUser sessionUser, ActiveUserStore activeUserStore) {
this.activeUserStore = activeUserStore;
this.sessionUser = sessionUser;
}
#Override
public void valueBound(HttpSessionBindingEvent event) {
HashMap<String, ActorUser> userDetails = activeUserStore.getUserDetails();
LoggedUser loggedUser = (LoggedUser) event.getValue();
if (isNotNull(userDetails) && !userDetails.containsKey(loggedUser.getSessionUser().getUsername())) {
userDetails.put(loggedUser.getSessionUser().getUsername(), loggedUser.getSessionUser());
}
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
LoggedUser loggedUser = (LoggedUser) event.getValue();
HashMap<String, ActorUser> userDetails = activeUserStore.getUserDetails();
if (isNotNull(userDetails) && isNotNull(loggedUser.getSessionUser()) &&
userDetails.containsKey(loggedUser.getSessionUser().getUsername())) {
userDetails.remove(loggedUser.getSessionUser().getUsername());
}
}
In order to solve this problem, I needed to create only one class.
package com.testsession.service;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.web.WebApplicationInitializer;
import javax.servlet.ServletContext;
public class MyWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
container.addListener(new HttpSessionEventPublisher());
}
}
I have implemented Webflux security by implementing:
ReactiveUserDetailsService
ReactiveAuthenticationManager
ServerSecurityContextRepository
Now, I am trying to introduce RoleHierarchy following the docs here: Role Hierarchy Docs
I have a user with role USER but he is getting 403 Denied on hitting a controller annotated with GUEST role. Role hierarchy is: "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST"
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
private final DaoAuthenticationManager reactiveAuthenticationManager;
private final SecurityContextRepository securityContextRepository;
private static final String ROLE_HIERARCHIES = "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST";
#Autowired
public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
SecurityContextRepository securityContextRepository) {
this.reactiveAuthenticationManager = reactiveAuthenticationManager;
this.securityContextRepository = securityContextRepository;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
#Bean(name = "roleHierarchy")
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(ROLE_HIERARCHIES);
return roleHierarchy;
}
#Bean(name = "roleVoter")
public RoleVoter roleVoter() {
return new RoleHierarchyVoter(roleHierarchy());
}
}
#Component
public class DaoAuthenticationManager implements ReactiveAuthenticationManager {
private final DaoUserDetailsService userDetailsService;
private final Scheduler scheduler;
#Autowired
public DaoAuthenticationManager(DaoUserDetailsService userDetailsService,
Scheduler scheduler) {
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.userDetailsService = userDetailsService;
this.scheduler = scheduler;
}
#Override
public Mono<Authentication> authenticate(Authentication authentication) {
final String username = authentication.getName();
return this.userDetailsService.findByUsername(username)
.publishOn(this.scheduler)
.switchIfEmpty(
Mono.defer(() -> Mono.error(new UsernameNotFoundException("Invalid Username"))))
.map(u -> new UsernamePasswordAuthenticationToken(u, u.getPassword(),
u.getAuthorities()));
}
}
#Component
public class SecurityContextRepository implements ServerSecurityContextRepository {
private final DaoAuthenticationManager authenticationManager;
#Autowired
public SecurityContextRepository(DaoAuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Mono<Void> save(ServerWebExchange swe, SecurityContext sc) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public Mono<SecurityContext> load(ServerWebExchange swe) {
ServerHttpRequest request = swe.getRequest();
if (request.getHeaders().containsKey("userName") &&
!Objects.requireNonNull(request.getHeaders().get("userName")).isEmpty()) {
String userName = Objects.requireNonNull(swe
.getRequest()
.getHeaders()
.get("userName")).get(0);
Authentication auth = new UsernamePasswordAuthenticationToken(userName,
Security.PASSWORD);
return this.authenticationManager.authenticate(auth).map(SecurityContextImpl::new);
} else {
return Mono.empty();
}
}
}
Anyway to get the role hierarchy thing working in Webflux security.
EDIT
Controller:
#GetMapping
#PreAuthorize("hasRole('USER')")
public Mono<Device> getDevice(#RequestParam String uuid) {
return deviceService.getDevice(uuid);
}
Normal role authorization is working for me, whats not working is the hierarchy part.
Here a very naive solution by overriding DefaultMethodSecurityExpressionHandler.
I supposed you annotated your controller with this king of expression : #PreAuthorize("hasRole('ROLE_USER')")
securityConfig.java
#Configuration
#EnableWebFluxSecurity
#EnableReactiveMethodSecurity
public class SecurityConfig {
private final DaoAuthenticationManager reactiveAuthenticationManager;
private final SecurityContextRepository securityContextRepository;
private static final String ROLE_HIERARCHY = "ROLE_ADMIN > ROLE_USER ROLE_USER > ROLE_GUEST";
#Autowired
public SecurityConfig(DaoAuthenticationManager reactiveAuthenticationManager,
SecurityContextRepository securityContextRepository) {
this.reactiveAuthenticationManager = reactiveAuthenticationManager;
this.securityContextRepository = securityContextRepository;
}
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(ROLE_HIERARCHY);
return roleHierarchy;
}
// Overriding spring default bean
#Bean
public DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setRoleHierarchy(roleHierarchy);
return handler;
}
}
Then you have to authorize spring bean overriding by modifying your application property file:
application.properties
spring.main.allow-bean-definition-overriding=true
Sources : issue 1 issue role hierarchy doc
Going a little bit further... This part can be optimized and cleaner.
Using url patterns setup from ServerHttpSecurity object.
Note that the following setup won't use role hierarchy :
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/user/**").hasRole("ROLE_USER") // This won't use role hierarchy because it will use implemention of hasRole defined in your 'reactiveAuthenticationManager'
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
A solution could be to create your own implementation of ReactiveAuthorizationManager and overriding check method in order to call access(...) from your http object (ServerHttpSecurity). Ie :
public class CustomReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
private final static Logger logger = LoggerFactory.getLogger(CustomReactiveAuthorizationManager.class);
private final RoleHierarchyVoter roleHierarchyVoter;
private final String authority;
CustomReactiveAuthorizationManager(String role, RoleHierarchy roleHierarchy) {
this.authority = ROLE_PREFIX + role;
this.roleHierarchyVoter = new RoleHierarchyVoter(roleHierarchy);
}
#Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T object) {
return authentication
.map(a -> {
ConfigAttribute ca = (ConfigAttribute) () -> authority;
int voteResult = roleHierarchyVoter.vote(a, object, Collections.singletonList(ca));
boolean isAuthorized = voteResult == AccessDecisionVoter.ACCESS_GRANTED;
return new AuthorizationDecision(isAuthorized);
})
.defaultIfEmpty(new AuthorizationDecision(false))
.doOnError(error -> logger.error("An error occured voting decision", error));
}
}
and then calling access method :
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, RoleHierarchy roleHierarchy() {
return http
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.authenticationManager(reactiveAuthenticationManager)
.securityContextRepository(securityContextRepository)
.authorizeExchange()
.pathMatchers("/user/**").access(new CustomReactiveAuthorizationManager<>("USER", roleHierarchy))
.anyExchange().permitAll()
.and()
.logout().disable()
.build();
}
One way I was able to achieve role hierarchy in Webflux was by creating custom annotations.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasRole('ADMIN')")
public #interface IsAdmin {
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole('ADMIN', 'USER')")
public #interface IsUser {
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole('ADMIN', 'USER', 'GUEST')")
public #interface IsGuest {
}
–––––––––––––––––
And annotating the controllers like this:
#GetMapping
#IsUser
public Mono<Device> getDevice(#RequestParam String uuid) {
return deviceService.getDevice(uuid);
}
#PostMapping
#IsAdmin
#ResponseStatus(HttpStatus.CREATED)
public Mono<Device> createDevice(#Valid #RequestBody Device device) {
return deviceService.createDevice(device);
}
I'm currently trying to build a database UI implementing spring security, but i'm stuck on how to change the intercept url access from access=hasRole('ROLE_ADMIN') to access=denyAll and deny any user from accessing that particular page without having me to logout.
this is my WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Value("${users-by-username-query}")
private String usersQuery;
#Value("${authorities-by-username-query}")
private String authoritiesQuery;
#Autowired
private MyAuthenticationHandler myAuthenticationHandler;
private CustomAccessDecisionManager customAccessDecisionManager;
#Autowired
private Service service;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<UrlRole> viewPermissions = service.findAllUrlRole();
System.out.println("Return from service class with size "+viewPermissions.size());
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http
// .authorizeRequests().antMatchers("/","/hello.html","/footer.jsp","/header.jsp","/sidebar.jsp","/reg_issuer.jsp","/reg_user.jsp","/rest/**","/IssuerList.jsp","/loginSecurity","/index.jsp","/verify_otp.jsp")
.authorizeRequests().antMatchers("/","/rest/**")
.permitAll();
for (int i = 0;i<viewPermissions.size();i++) {
String url = viewPermissions.get(i).getUrl();
String string = "";
if(viewPermissions.get(i).getRole().equalsIgnoreCase("denyAll")){
string = viewPermissions.get(i).getRole();
}else{
string = "hasRole('"+viewPermissions.get(i).getRole()+"')";
for (int j = 0;j<viewPermissions.size();j++) {
if(j!=i && viewPermissions.get(j).getUrl().equalsIgnoreCase(url) ){
string+=" or hasRole('"+viewPermissions.get(j).getRole()+"')";
}
}
}
interceptUrlRegistry.antMatchers(viewPermissions.get(i).getUrl()).access(string);
}
interceptUrlRegistry.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").successHandler(myAuthenticationHandler)
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(authoritiesQuery);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/assets/**");
}
}
Currently i am looping the "antMatchers(viewPermissions.get(i).getUrl()).access(string)" to get the url and roles from database but it only been done when the first time i deploy it in wildfly. That is why the new access for url will not be implemented unless i restart the wildfly server.
Is there anyway to implement it without having to restart the server?
THE ANSWERED I GOT AND WORKS FOR ME IS AS BELOW.
this is my new WebSecurityConfig class:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Value("${users-by-username-query}")
private String usersQuery;
#Value("${authorities-by-username-query}")
private String authoritiesQuery;
#Autowired
private MyAuthenticationHandler myAuthenticationHandler;
#Autowired
private Service service;
#Override
protected void configure(HttpSecurity http) throws Exception {
List<UrlRole> viewPermissions = service.findAllUrlRole();
System.out.println("Return from service class with size "+viewPermissions.size());
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http
.authorizeRequests().antMatchers("/rest/**")
.permitAll();
interceptUrlRegistry.antMatchers("/login").access("hasRole('ROLE_ANONYMOUS')");
interceptUrlRegistry.anyRequest().authenticated().accessDecisionManager(accessDecisionManager())
.and()
.formLogin()
.loginPage("/login").successHandler(myAuthenticationHandler)
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/403");
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(authoritiesQuery);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**", "/assets/**","/rest/findAllUrlRole","/error","/403","/404","/500");
}
#SuppressWarnings("unchecked")
#Bean
public AccessDecisionManager accessDecisionManager() {
System.out.println("Arrive AccessDecisionManager");
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new WebExpressionVoter(),
new RoleVoter(),
new AuthenticatedVoter(),
new MinuteBasedVoter());
System.out.println("End of AccessDecisionManager: "+ decisionVoters);
return new UnanimousBased(decisionVoters);
}
}
this is my MinuteBasedVoter class:
#SuppressWarnings("rawtypes")
public class MinuteBasedVoter implements AccessDecisionVoter {
#Override
public int vote(
Authentication authentication, Object object, Collection collection) {
WebServiceTester a = new WebServiceTester();
String username = authentication.getName(); //to get current user role
String url = ((FilterInvocation) object).getRequestUrl(); // to get current url
boolean NONanonymous = true;
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if(grantedAuthority.getAuthority().equalsIgnoreCase("ROLE_ANONYMOUS")){
NONanonymous = false;
}
}
int vote = ACCESS_ABSTAIN;
boolean NONexist = true;
if(NONanonymous){
List<Role> roles = new ArrayList<Role>();
Role role = new Role();
vote = ACCESS_DENIED;
try{
List<UrlRole> urlroles = a.findAllUrlRole(); // to get all url and its respective role
// below is how i match the role of current user and the role that can access the current url
for(int i = 0; i<urlroles.size();i++){
if(url.startsWith(urlroles.get(i).getUrl())){
NONexist = false;
System.out.println("URL: "+url+" , Role: "+urlroles.get(i).getRole());
role.setRole(urlroles.get(i).getRole());
roles.add(role);
for (GrantedAuthority grantedAuthority : authorities) {
if(grantedAuthority.getAuthority().equalsIgnoreCase(urlroles.get(i).getRole())){
vote = ACCESS_GRANTED;
}
}
}
}
}catch(Exception e){
System.out.println("Error at MinuteBasedVoter: "+e);
}
if(NONexist){
vote = ACCESS_GRANTED;
}
}
return vote;
}
#Override
public boolean supports(ConfigAttribute attribute) {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean supports(Class clazz) {
// TODO Auto-generated method stub
return true;
}
}
i got this solution from http://www.baeldung.com/spring-security-custom-voter but with a twist of my own.