Specific case of springboot security, implementation of security framework, permission control, aop entry

Posted by maxsslade on Fri, 04 Mar 2022 10:34:27 +0100

Specific case of springboot security, implementation of security framework, permission control, aop entry

Introduction to springboot security

Security official Preface

Security is an ever-changing goal, and it is important to pursue a comprehensive and system wide approach. We encourage you to adopt additional layers of security in each area, and we can ensure that you can use such layers of security as much as possible. The more "strict" the security of each layer, the more robust and secure your application will be. At the bottom, in order to reduce man in the middle attacks, you need to deal with issues such as transmission security and system identification. Next, you will usually use a firewall, perhaps through vpn or IP security to ensure that only authorized systems can try to connect. In a corporate environment, you can deploy DMZ to separate public facing servers from back-end database and application servers. Your operating system will also play a key role in addressing issues such as running processes as an unprivileged user and maximizing file system security. The operating system usually also configures its own firewall. Hopefully, somewhere, you can try to prevent distributed denial of service attacks and brute force cracking against the system. Intrusion prevention system security protocol is also particularly useful for monitoring and responding to attacks. Such a system can take protective measures, such as blocking illegal TCP/IP addresses in real time. Moving to a higher tier, your Java virtual machine is expected to be configured to minimize granting permissions to different Java types, and then your application will add its own problem domain specific security configuration. Spring Security makes the latter area -- Application Security -- easier.

There are many reasons why people use Spring Security, but most people start using this project after discovering the security features of Java EE Servlet specification or EJB specification. These features lack the depth required by typical enterprise application scenarios. Although these standards are mentioned, it is important to recognize that they are not portable at the WAR or EAR level. Therefore, if the server environment is switched, it usually requires a lot of work to reconfigure the security of the application in the new target environment. Using Spring Security overcomes these problems and brings you many other useful and customizable security features.

As you may know, the two main aspects of application security are "authentication" and "authorization" (or "access control"). These are the two main goals of Spring Security. "Authentication" is the process of establishing a principal, which is the person they claim ("principal" usually refers to a user, device or other system that can perform an operation in your application). "Authorization" refers to the process of determining whether a principal is allowed to perform operations in an application. In order to reach the point where authorization decisions need to be made, the authentication process has determined the identity of the subject. These concepts are common and are not unique to Spring Security at all.

(I like it very much when I read it myself. I'll add it at the beginning of this article.)

The article is in security 5.0.5 See in the release version document.

https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/htmlsingle/

Article introduction

This article is about the Spring Boot integrated spring security framework. The content does not involve too much explanation of specific principles, but only a brief overview.

Suitable for beginners, the status is probably: I don't know much about this, but I need to use spring boot security in the project for the time being.

In the case, there are specific database role table, permission table and resource table, which can be cut into existing projects.

And it's a way to add annotations.

Process, principle, etc. I should also write drops. Believe me... I'm still very curious about this process and principle.

Of course, after reading it, I'm curious. Go to Debug right away, Ollie!!!

There is a line of code in the project that uses the Optional in jdk 8. I wrote an article to explain it before

This is just a practical one. You can have a look.

https://blog.csdn.net/weixin_45821811/article/details/115656637

Database and table

I have finished writing the article of table before. as follows

https://blog.csdn.net/weixin_45821811/article/details/115737401

step

1. Create a spring boot project

Engineering structure drawing

2. Import dependency

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
    </dependencies>

3. yaml profile

server:
  port: 8787

spring:
  datasource: #Own database name own database password  
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useSSL=false&characterEncoding=utf8&serverTimezone=GMT
    password: 123456
    username: root

4,config

  • WebConfig

    @Configuration  // Equivalent to spring MVC file
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("redirect:/login");
        }
    }
    
    
  • WebSecurityConfig security configuration

    @Configuration
    @EnableWebSecurity  // Turn on WebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)  // This open permission annotation
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable(). //Shielded csrf
                    authorizeRequests()
    //                .antMatchers("/r/r1").hasAnyAuthority("p1") / / this method does not enable permission annotation and does not add permission annotation
    //                .antMatchers("/r/r2").hasAnyAuthority("p2")
    //                .antMatchers("/r/r3").hasAnyAuthority("p1","p2")
                    .antMatchers("/r/**").authenticated()
                    .anyRequest().permitAll()
                    .and()
                    .formLogin()
                    .permitAll()
                    .and()
                    .logout()
                    .logoutUrl("/logout") // Exit url
                    .logoutSuccessUrl("/logout-success");  // After the user-defined login configuration is successful, turn to this url
        }
    }
    
    

5. pojo layer

  • MyUser user

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Accessors(chain = true)// Chain type
    public class MyUser {
        private String id;
        private String username;
        private String password;
        private String fullname;
        private String mobile;
    }
    
    
  • PermissionDto resource permissions

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Accessors(chain = true)// Chain type
    public class PermissionDto {
        private String id;
        private String code;
        private String description;
        private String url;
    }
    

6. Dao layer

@Mapper
public interface MyUserDao {

    @Select("select * from user_db where username=#{username}")
    Optional<MyUser> getUserByName(String username);

    // Here is a multi table associated query. The structure of the permission table owned by the current role is described in another article
    @Select("SELECT * FROM t_permission WHERE id IN( SELECT permission_id FROM t_role_permission WHERE role_id IN(SELECT role_id FROM t_user_role WHERE user_id = #{userId} ))")
    List<PermissionDto> findPermissionsByUserId(String userId);
}

7,Service

@Configuration
public class SpringDataMyUserDetailsService implements UserDetailsService {

    @Autowired
    MyUserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<MyUser> user = userDao.getUserByName(username);
        if (user.isPresent()) {// This is a new feature of jdk8, which is described in my article
            MyUser myUser = user.get(); // Take out operation

            List<PermissionDto> list = userDao.findPermissionsByUserId(myUser.getId());
            System.out.println("list==>" + list.toString());
            //List = = > [permissiondto (id = 1, code = P1, description = test resource 1, url=/r/r1)]

            List<String> permissions = new ArrayList<>();
            list.forEach(p -> permissions.add(p.getCode()));
            System.out.println("permissions===>" + permissions.toString());
            //permissions===>[p1]
            // The reason why it is converted to an array here is that when building UserDetails
            //. authorities(perarray) where permissions are added, an array can be added, which is more convenient for us to customize
            //Parameter: permission – the permission of this user (i.e. ROLE_USER, ROLE_ADMIN, etc.). Cannot be null or contain null values
            //		public UserBuilder authorities(String... authorities) {
            //			return authorities(AuthorityUtils.createAuthorityList(authorities));
            //		}


            String[] perarray = new String[permissions.size()];

            permissions.toArray(perarray);
            //Create userDetails
            UserDetails userDetails =
                    User.withUsername(myUser.getUsername()).password( BCrypt.hashpw(myUser.getPassword(), BCrypt.gensalt()) ).authorities(perarray).build();
            return userDetails;
        }
        return null;
    }
}

8. Controller layer

@RestController
public class LoginController {

    //You all know the back-end return type. I write the text here. json is application/json and the request format. I mostly copy it, not the restful style
    // You can modify it by yourself
    @RequestMapping(value = "/login-success", produces = "text/plain;charset=utf-8")
    public String loginSuccess(){
        return getUsername()+"Login successful";
    }

    @RequestMapping(value = "/logout-success", produces = "text/plain;charset=utf-8")
    public String logout(){
        return "Exit successful";
    }


    @RequestMapping(value = "/r/r1", produces = "text/plain;charset=utf-8")
//    @PreAuthorize("isAnonymous()") / / anonymous access
    @PreAuthorize("hasAuthority('p1')" )
    public String r1(){
        return getUsername()+"Resource 1";
    }
    @RequestMapping(value = "/r/r2", produces = "text/plain;charset=utf-8")
    @PreAuthorize(" hasAuthority('p2')" )
    public String r2(){
        return getUsername()+"Resource 2";
    }

    @RequestMapping(value = "/r/r3", produces = "text/plain;charset=utf-8")
    @PreAuthorize("hasAuthority('p1') or hasAuthority('p2')" )
    public String r3(){
        return getUsername()+"Resource 3";
    }

    // This is that you can access as long as you pass the authentication, and you don't need the permission of the subject
    @RequestMapping(value = "/r/r4", produces = "text/plain;charset=utf-8")
    public String r4(){
        return getUsername()+"Resources 4";
    }

    private String getUsername(){
        String username=null;
        //Current user identity
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Object principal = authentication.getPrincipal();
        if (principal==null){
            username="anonymous";
        }
        if(principal instanceof UserDetails){
            UserDetails userDetails=  (UserDetails)principal;
            username = userDetails.getUsername();
        }else{
            username= principal.toString();
        }

        return username;
    }
}

9. Startup class

@SpringBootApplication
@MapperScan("com.wyh.dao")
public class SecuritySpringbootApplication {

    public static void main(String[] args) {
        SpringApplication.run(SecuritySpringbootApplication.class, args);
    }

}

Next is the test. And my self talk

test

authentication

Authentication successful


Authentication error

sign out

to grant authorization

admin has p1 permission and can access / r/r1 resources


admin does not have p2 permission and cannot access the / r/r2 resource

/r/r3 can be accessed with p1 permission or p2 permission, but it cannot be accessed without it


/r/r4 can be accessed as long as the identity authentication is passed


think aloud

Record your life every day and get used to writing here and there every day.

At first, I wanted to make it convenient for myself to read, but when I wrote, I began to want people to pay attention to it.

It's better than casually writing the current words at the beginning.

I suddenly want to record what I know. Maybe I want people to know.

There are so many things to learn.

Keep going.

Topics: Java Spring Spring Boot Back-end security