Spring security

The mind map is as follows

RBAC authority analysis

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

Draw a mind map as follows

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 exclusion

In 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 constraints

In 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.

precondition

That 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 mutex

In 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 use

Add 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-7d32123d0f9c

Login 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("{noop}" + "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 JWT

This is a small demo for the purpose of returning the token generated by jwt after login

Import dependency

Add web 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 login

The 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 method

You 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 authentication

Spring 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

Summary

This 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,

Tags: Spring Java JSON Database

Posted on Mon, 29 Jun 2020 23:20:10 -0400 by Ward