In this
post, I am going to explain how to secure web application using user
roles/authorizations.
Step 1:
Create User Entity.
@Entity @Table(name = "user") public class User { @Id @Column(name = "user_id") @GeneratedValue private int id; @Column(name = "user_name", nullable = false, unique = true) private String userName; @Column(name = "user_password", nullable = false) private String password; ...... ...... }
Step 2:
Create UserAuthorizations
entity.
@Entity @Table(name = "user_authorizations") public class UserAuthorizations { @Id @Column(name = "user_auth_group_id") @GeneratedValue private int id; @Column(name = "user_name") private String userName; @Column(name = "auth_group") private String authGroup; ..... ..... }
'authGroup'
property specifies the role of the user.
public class UserPrincipal implements UserDetails { @Override public Collection<? extends GrantedAuthority> getAuthorities() { if(authGroups == null) return Collections.EMPTY_SET; Set<GrantedAuthority> grantedAuthorities = new HashSet<> (); for(UserAuthorizations authGroup: authGroups) { grantedAuthorities.add(new SimpleGrantedAuthority(authGroup.getAuthGroup())); } return grantedAuthorities; } ...... ...... }
Step 4:
Implement UserDetailsService
service.
@Service public class UserDetailsServiceImpl implements UserDetailsService{ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUserName(username); if(user == null) { throw new UsernameNotFoundException("User not exist"); } List<UserAuthorizations> authGroups = authGroupRepository.findByUserName(user.getUserName()); return new UserPrincipal(user, authGroups); } }
Step 5: Use '@PreAuthorize' annotation to set the roles on APIs.
@RestController @RequestMapping("employees/") public class EmployeeController { @RequestMapping(value = "registered/count", method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN')") public String countEmps() { return "Total Registered Employees : "+ 1024; } @RequestMapping(value = "greetMe", method = RequestMethod.GET) @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public String greetMe() { return "Very Good Day to you"; } }
Step 6: Extend 'WebSecurityConfigurerAdapter' and provide a bean of type GrantedAuthoritiesMapper.
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class ApplicationSecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); daoAuthenticationProvider.setUserDetailsService(userDetailsService); daoAuthenticationProvider.setPasswordEncoder(passwordEncoder); daoAuthenticationProvider.setAuthoritiesMapper(authoritiesMapper()); return daoAuthenticationProvider; } @Bean public GrantedAuthoritiesMapper authoritiesMapper(){ SimpleAuthorityMapper authorityMapper = new SimpleAuthorityMapper(); authorityMapper.setConvertToUpperCase(true); authorityMapper.setDefaultAuthority("ROLE_USER"); return authorityMapper; } }
Total
project structure looks like below.
Find the
below working application.
package com.sample.app.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "user") public class User { @Id @Column(name = "user_id") @GeneratedValue private int id; @Column(name = "user_name", nullable = false, unique = true) private String userName; @Column(name = "user_password", nullable = false) private String password; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
UserAuthorizations.java
package com.sample.app.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "user_authorizations") public class UserAuthorizations { @Id @Column(name = "user_auth_group_id") @GeneratedValue private int id; @Column(name = "user_name") private String userName; @Column(name = "auth_group") private String authGroup; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getAuthGroup() { return authGroup; } public void setAuthGroup(String authGroup) { this.authGroup = authGroup; } }
UserPrincipal.java
package com.sample.app.model; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; public class UserPrincipal implements UserDetails { private static final long serialVersionUID = 1L; private User user; private List<UserAuthorizations> authGroups; public UserPrincipal(final User user, final List<UserAuthorizations> authGroups) { super(); this.user = user; this.authGroups = authGroups; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { if(authGroups == null) return Collections.EMPTY_SET; Set<GrantedAuthority> grantedAuthorities = new HashSet<> (); for(UserAuthorizations authGroup: authGroups) { grantedAuthorities.add(new SimpleGrantedAuthority(authGroup.getAuthGroup())); } return grantedAuthorities; } @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUserName(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
UserAuthorizationRepository.java
package com.sample.app.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import com.sample.app.model.UserAuthorizations; public interface UserAuthorizationRepository extends JpaRepository<UserAuthorizations, Integer>{ List<UserAuthorizations> findByUserName(String userName); }
UserRepository.java
package com.sample.app.repository; import org.springframework.data.jpa.repository.JpaRepository; import com.sample.app.model.User; public interface UserRepository extends JpaRepository<User, Integer> { User findByUserName(String userName); }
UserDetailsServiceImpl.java
package com.sample.app.service; import java.util.List; 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.Service; import com.sample.app.model.UserAuthorizations; import com.sample.app.model.User; import com.sample.app.model.UserPrincipal; import com.sample.app.repository.UserAuthorizationRepository; import com.sample.app.repository.UserRepository; @Service public class UserDetailsServiceImpl implements UserDetailsService{ @Autowired private UserRepository userRepository; @Autowired private UserAuthorizationRepository authGroupRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUserName(username); if(user == null) { throw new UsernameNotFoundException("User not exist"); } List<UserAuthorizations> authGroups = authGroupRepository.findByUserName(user.getUserName()); return new UserPrincipal(user, authGroups); } }
ApplicationSecurityConfiguration.java
package com.sample.app.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.crypto.password.PasswordEncoder; import com.sample.app.service.UserDetailsServiceImpl; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class ApplicationSecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); daoAuthenticationProvider.setUserDetailsService(userDetailsService); daoAuthenticationProvider.setPasswordEncoder(passwordEncoder); daoAuthenticationProvider.setAuthoritiesMapper(authoritiesMapper()); return daoAuthenticationProvider; } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf().disable().authorizeRequests().antMatchers("/", "/public/*", "/css/*", "/js/*").permitAll() .anyRequest().authenticated().and().httpBasic(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ auth.authenticationProvider(authenticationProvider()); } @Bean public GrantedAuthoritiesMapper authoritiesMapper(){ SimpleAuthorityMapper authorityMapper = new SimpleAuthorityMapper(); authorityMapper.setConvertToUpperCase(true); authorityMapper.setDefaultAuthority("ROLE_USER"); return authorityMapper; } }
EmployeeController.java
package com.sample.app.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("employees/") public class EmployeeController { @RequestMapping(value = "registered/count", method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN')") public String countEmps() { return "Total Registered Employees : "+ 1024; } @RequestMapping(value = "greetMe", method = RequestMethod.GET) @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public String greetMe() { return "Very Good Day to you"; } }
HelloWorldController.java
package com.sample.app.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloWorldController { @RequestMapping("/") public String homePage() { return "Welcome to Spring boot Application Development using Spring Security"; } @RequestMapping("/public/aboutme") public String aboutMe() { return "I am securied by spring security module"; } }
App.java
package com.sample.app; import com.sample.app.model.User; import com.sample.app.model.UserAuthorizations; import java.util.Arrays; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import com.sample.app.repository.UserAuthorizationRepository; import com.sample.app.repository.UserRepository; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Bean public CommandLineRunner demo(UserRepository userRepository, UserAuthorizationRepository userAuthorizationRepository) { return (args) -> { User user1 = new User(); user1.setUserName("krishna"); user1.setPassword(passwordEncoder().encode("password123")); User user2 = new User(); user2.setUserName("ram"); user2.setPassword(passwordEncoder().encode("ram123")); UserAuthorizations userAuthorization1 = new UserAuthorizations(); userAuthorization1.setUserName("krishna"); userAuthorization1.setAuthGroup("ROLE_ADMIN"); UserAuthorizations userAuthorization2 = new UserAuthorizations(); userAuthorization2.setUserName("ram"); userAuthorization2.setAuthGroup("ROLE_USER"); userRepository.saveAll(Arrays.asList(user1, user2)); userAuthorizationRepository.saveAll(Arrays.asList(userAuthorization1, userAuthorization2)); }; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(16); } }
application.properties
logging.level.root=WARN logging.level.org.hibernate=ERROR ## H2 specific properties spring.h2.console.enabled=true spring.h2.console.path=/h2 spring.datasource.url=jdbc:h2:file:~/db/myOrg.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1; spring.datasource.username=krishna spring.datasource.password=password123 spring.datasource.driverClassName=org.h2.Driver ## JPA specific properties # Creates the schema, destroying previous data. spring.jpa.hibernate.ddl-auto=create spring.jpa.database-platform=org.hibernate.dialect.H2Dialect #spring.jpa.show-sql=true #spring.jpa.properties.hibernate.format_sql=true ## Database connection pooling properties # Number of ms to wait before throwing an exception if no connection is available. spring.datasource.max-wait=10000 # Maximum number of active connections that can be allocated from this pool at the same time. spring.datasource.tomcat.max-active=10 spring.datasource.tomcat.max-idle=5 spring.datasource.tomcat.min-idle=3 spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>springSecurity</groupId> <artifactId>springSecurity</artifactId> <version>1</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> </dependencies> </project>
Run
App.java.
Open the
url 'http://localhost:8080/employees/greetMe' in browser.
Enter user
name as 'ram' and password as 'ram123' and click on OK button.
Open the
url 'http://localhost:8080/employees/registered/count' in the same browser,
since ram has only USER access, you will get 'Forbidden' response.
You can download
complete working application from this link.
No comments:
Post a Comment