RBAC is the full name of role-based permission control. This section will describe RBAC from the following aspects: what is RBAC, model classification, what is permission, the use of user groups, and case analysis
Mind map
What is RBAC
RBAC is the full name of user role permission control. Through the role associated user, role associated permission. In this way, the user's authority is given in an intermediate level, as shown in the figure below
For the common system, there are multiple users with the same permissions. When assigning, you need to assign the relevant permissions to the specified users. When you modify the permissions, you need to modify the permissions of these users in turn. With the permission of role, you only need to modify the roles to modify the relevant permissions. This increases the efficiency and reduces the occurrence of privilege vulnerabilities.
Model classification
For RBAC model, it is divided into the following four models: RBAC0, RBAC1, RBAC2, RBAC3. This section will introduce these four models in turn, among which the most commonly used model is RBAC0
RBAC0
RBAC0 is the simplest RBAC model, which contains two kinds.
The relationship between users and roles is many to one, that is, a user only acts as one role, and a role can have multiple roles.
The relationship between users and roles is many to many, that is, a user can act as multiple roles at the same time, and a role can have multiple users.
This system has a single function and few personnel. Here is a chestnut. Zhang San is not only in charge of administration but also in charge of finance. At this time, Zhang San has two authorities, namely, administrative authority and financial authority.
RBAC1
Compared with RBAC0 model, the sub role is added and the concept of inheritance is introduced.
RBAC2 model
Here, RBAC2 model, based on RBAC0 model, adds some functions and limitations
Role exclusionIn other words, the same user cannot have two mutually exclusive roles. For example, in the financial system, a user cannot have the two roles of accountant and audit.
cardinality constraintsIn other words, with a role, the members are fixed. For example, for the role of CEO, there can only be one user in the same role.
preconditionThat is, for the role, if you want to get a higher role, you need to get a lower level role first. For example, for the two authorities of deputy general manager and manager, the authority of deputy general manager is the prerequisite of manager authority.
Runtime mutexIn other words, a user can have two roles, but these two roles can not be used at the same time, so you need to switch roles to enter another role. For example, for the two roles of the general manager and the Commissioner, the system can only have one role for a period of time, and can not operate the two roles at the same time.
RBAC3 model
The first mock exam is RBAC1, RBAC2, and the two models are called unified models.
What is authority
Permission is the collection of resources. The resources here refer to all the contents in the software, that is, the operation authority on the page, the access right to the page, and the authority to add, delete, check and modify the data. Take a chestnut. For the system in the figure below,
[the transfer of the external chain image failed. The source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-flfsrsfc-1593481853145)( https://www.iming.info/wp-content/uploads/2020/06/wp_ editor_ Md_ dce317e2c04d0a6dfc8eb1461d997831.jpg)]
Access to several pages of grain management, such as food management, management, management, management, management and management of grain.
Use of user groups
For user groups, it is to divide many users into a group and grant roles in batch, that is, to grant permissions in batch. For example, for a department, there are more than 10000 employees in a department. These employees all have the same roles. If there is no user group, you may need to grant related roles one by one. After having user groups, you only need to divide all these users into a group, and then setting up granting roles to this group is equivalent to granting roles to these users.
Advantages: reduce workload, easy to understand, increase multi-level management, etc.
Spring security is easy to useAdd dependencies first
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
Then add the relevant provider
package com.example.demo.web; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/test") public class Test { @RequestMapping("/test") public String test(){ return "test"; } }
Finally, start the project and check the relevant password in the log
Access interface, you can see the relevant login interface
Enter the user name and associated password
User name: user Password 984ccccf2-ba82-468e-a404-7d32123d0f9cLogin successful
Add user name and password
In the configuration file, write the relevant login and password
spring: security: user: name: ming password: 123456 roles: admin
In the login page, enter the user name and password, you can log in normally
Memory based authentication
The custom class needs to inherit WebSecurityConfigurerAdapter. The code is as follows
package com.example.demo.config; 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.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder(){ return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin").password("123").roles("admin"); } }
That is, the configured user name is admin, the password is 123, and the role is admin
HttpSecurity
Some methods are intercepted here
package com.ming.demo.interceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { //Memory based user storage @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("itguang").password("123456").roles("USER").and() .withUser("admin").password("" + "123456").roles("ADMIN"); } //Request interception @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().permitAll() .and() .formLogin() .permitAll() .and() .logout() .permitAll(); } }
That is, the interception of all method access is completed here.
Spring security integrates JWTThis is a small demo for the purpose of returning the token generated by jwt after login
Import dependency
Import JWT and Security dependencies
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.3.1.RELEASE</version> </dependency>
Create a juser implementation of details
Create a related JavaBean
package com.example.demo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; public class JwtUser implements UserDetails { private String username; private String password; private Integer state; private Collection<? extends GrantedAuthority> authorities; public JwtUser(){ } public JwtUser(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities){ this.username = username; this.password = password; this.state = state; this.authorities = authorities; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
Write tool class to generate token
Write a tool class to generate token, refresh token and verify token
package com.example.demo; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.UserDetails; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtTokenUtil implements Serializable { private String secret; private Long expiration; private String header; private String generateToken(Map<String, Object> claims) { Date expirationDate = new Date(System.currentTimeMillis() + expiration); return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact(); } private Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } catch (Exception e) { claims = null; } return claims; } public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(2); claims.put("sub", userDetails.getUsername()); claims.put("created", new Date()); return generateToken(claims); } public String getUsernameFromToken(String token) { String username; try { Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } public Boolean isTokenExpired(String token) { try { Claims claims = getClaimsFromToken(token); Date expiration = claims.getExpiration(); return expiration.before(new Date()); } catch (Exception e) { return false; } } public String refreshToken(String token) { String refreshedToken; try { Claims claims = getClaimsFromToken(token); claims.put("created", new Date()); refreshedToken = generateToken(claims); } catch (Exception e) { refreshedToken = null; } return refreshedToken; } public Boolean validateToken(String token, UserDetails userDetails) { JwtUser user = (JwtUser) userDetails; String username = getUsernameFromToken(token); return (username.equals(user.getUsername()) && !isTokenExpired(token)); } }
Write interceptors
Write Filter to detect JWT
package com.example.demo; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String authHeader = httpServletRequest.getHeader(jwtTokenUtil.getHeader()); if (authHeader != null && StringUtils.isNotEmpty(authHeader)) { String username = jwtTokenUtil.getUsernameFromToken(authHeader); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(authHeader, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(httpServletRequest, httpServletResponse); } }
Write the implementation class of userDetailsService
In the above code, write the userDetailsService, class, and implement its verification process
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; 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 javax.management.relation.Role; import java.util.List; @Service public class JwtUserDetailsServiceImpl implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { User user = userMapper.selectByUserName(s); if (user == null) { throw new UsernameNotFoundException(String.format("'%s'.This user does not exist", s)); } List<SimpleGrantedAuthority> collect = user.getRoles().stream().map(Role::getRolename).map(SimpleGrantedAuthority::new).collect(Collectors.toList()); return new JwtUser(user.getUsername(), user.getPassword(), user.getState(), collect); } }
Write login
Write the implementation class of login service, and its login method will return a JWTUtils token
@Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; public User findByUsername(String username) { User user = userMapper.selectByUserName(username); return user; } public RetResult login(String username, String password) throws AuthenticationException { UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password); final Authentication authentication = authenticationManager.authenticate(upToken); SecurityContextHolder.getContext().setAuthentication(authentication); UserDetails userDetails = userDetailsService.loadUserByUsername(username); return new RetResult(RetCode.SUCCESS.getCode(),jwtTokenUtil.generateToken(userDetails)); } }
Finally, configure Config
@EnableGlobalMethodSecurity(prePostEnabled = true) @EnableWebSecurity public class WebSecurity extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder()); } @Bean(name = BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers("/auth/**").permitAll() .anyRequest().authenticated() .and().headers().cacheControl(); http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests(); registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll(); } @Bean public CorsFilter corsFilter() { final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); final CorsConfiguration cors = new CorsConfiguration(); cors.setAllowCredentials(true); cors.addAllowedOrigin("*"); cors.addAllowedHeader("*"); cors.addAllowedMethod("*"); urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors); return new CorsFilter(urlBasedCorsConfigurationSource); } }
Run, return token
Run. The returned result is token [the transfer of the external chain image failed. The source station may have anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-t0cs2kgd-1593481853175)( https://www.iming.info/wp-content/uploads/2020/06/wp_ editor_ Md_ ec49f8d680cf76177fa79baa2f561e5b.jpg)]
Spring security JSON loginThe JSON login of spring security is configured here
Here you need to override the usernamepasswordauthenticationfilter class and configure spring security
Override usernamepasswordauthentication filter
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { //attempt Authentication when Content-Type is json if(request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) ||request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){ //use jackson to deserialize json ObjectMapper mapper = new ObjectMapper(); UsernamePasswordAuthenticationToken authRequest = null; try (InputStream is = request.getInputStream()){ AuthenticationBean authenticationBean = mapper.readValue(is,AuthenticationBean.class); authRequest = new UsernamePasswordAuthenticationToken( authenticationBean.getUsername(), authenticationBean.getPassword()); }catch (IOException e) { e.printStackTrace(); authRequest = new UsernamePasswordAuthenticationToken( "", ""); }finally { setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } } //transmit it to UsernamePasswordAuthenticationFilter else { return super.attemptAuthentication(request, response); } } }
Configure SecurityConfig
@Override protected void configure(HttpSecurity http) throws Exception { http .cors().and() .antMatcher("/**").authorizeRequests() .antMatchers("/", "/login**").permitAll() .anyRequest().authenticated() //formLogin() must be written here, otherwise the original UsernamePasswordAuthenticationFilter will not appear, and we will not be able to configure our new UsernamePasswordAuthenticationFilter .and().formLogin().loginPage("/") .and().csrf().disable(); //Replace the old UsernamePasswordAuthenticationFilter with the rewritten Filter http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } //Register a custom UsernamePasswordAuthenticationFilter @Bean CustomAuthenticationFilter customAuthenticationFilter() throws Exception { CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); filter.setAuthenticationSuccessHandler(new SuccessHandler()); filter.setAuthenticationFailureHandler(new FailureHandler()); filter.setFilterProcessesUrl("/login/self"); //This sentence is very important. Reuse the authentication manager configured by websecurity configureradapter, otherwise, you have to assemble the authentication manager yourself filter.setAuthenticationManager(authenticationManagerBean()); return filter; }
This completes logging in to spring security using json
Spring Security password encryption methodYou need to configure the following in the Config class
/** * Password encryption */ @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
That is, use this method to encrypt the password, and use this encryption method in the business layer
@Service @Transactional public class UserServiceImpl implements UserService { @Resource private UserRepository userRepository; @Resource private BCryptPasswordEncoder bCryptPasswordEncoder; //Injection bcryct encryption @Override public User add(User user) { user.setPassword(bCryptPasswordEncoder.encode(user.getPassword())); //Encrypt password User user2 = userRepository.save(user); return user2; } @Override public ResultInfo login(User user) { ResultInfo resultInfo=new ResultInfo(); User user2 = userRepository.findByName(user.getName()); if (user2==null) { resultInfo.setCode("-1"); resultInfo.setMessage("user name does not exist"); return resultInfo; } //Determine whether the password is correct if (!bCryptPasswordEncoder.matches(user.getPassword(),user2.getPassword())) { resultInfo.setCode("-1"); resultInfo.setMessage("The password is incorrect"); return resultInfo; } resultInfo.setMessage("Login successful"); return resultInfo; } }
In other words, BCryptPasswordEncoder is used to encrypt the password and save the database
Using database authenticationSpring security is used for database authentication
Design data sheet
Here we design the data table
Focus on configuration of SpringConfig
@Configurable public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; // service layer injection @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // Parameters are passed into Service for verification auth.userDetailsService(userService); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/login").permitAll() .and() .csrf().disable(); } }
Here we focus on configuring SpringConfig
SummaryThis paper focuses on the authority configuration of RBAC, the simple use of spring security, the separation of the front end and the back end using spring Security + JWT, and the configuration of json login and password encryption,