Introduction to the

Security has always been a very important aspect of Web development.

Security, while a non-functional requirement of an application, should be considered early in application development.

There are some famous ones in the market: Shiro, Spring Security!

Let’s take a look at Spring Security’s official address

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications. Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements Spring Security is a powerful and highly customizable authentication and access control framework. It is essentially a standard for securing Spring-based applications. Spring Security is a framework that focuses on providing authentication and authorization for Java applications. As with all Spring projects, the real power of Spring security is that it can be easily extended to meet custom requirementsCopy the code

From the introduction of the official website we know that this is a permission framework.

Spring Security provides a complete solution for Web application Security based on the Spring framework.

Generally speaking, the Security of Web applications includes Authentication and Authorization, both of which are well supported by Spring Security framework.

In terms of user authentication, the Spring Security framework supports mainstream authentication methods, including HTTP basic authentication, HTTP form authentication, HTTP digest authentication, OpenID and LDAP. On the user authorization side, Spring Security provides role-based Access Control and Access Control Lists (ACLs) for fine-grained Control of domain objects in applications.Copy the code

Construction of actual combat environment

Project code address: gitee.com/zwtgit/spri…

1, create an initial Springboot project Web module, thymeleaf module

2, import static resources, resources above

3, Controller jump!

@Controller
public class RouterController {

   @RequestMapping({"/","/index"})
   public String index(a){
       return "index"; }}Copy the code

4, test whether the experimental environment is OK!

SpringSecurity

Spring Security is a Security framework for Spring projects and the default technology selection for Spring Boot underlying Security modules.

It can achieve powerful Web security control, for security control, we only need to introduce the spring-boot-starter-Security module, a small amount of configuration, you can achieve powerful security management!

Keep a few classes in mind:

  • WebSecurityConfigurerAdapter: custom Security strategy
  • AuthenticationManagerBuilder: custom authentication strategy
  • @enableWebSecurity: Enable the WebSecurity mode

The two main goals of Spring Security are “authentication” and “authorization” (access control).

Authentication

Authentication is about validating your credentials, such as username/user ID and password, to verify your identity.

Authentication is usually done with a username and password, sometimes in combination with an authentication factor.

Authorization

Authorization occurs when the system successfully verifies your identity, eventually granting you full access to resources such as information, files, databases, funds, locations, and just about anything.

This concept is universal, not unique to Spring Security.

Authentication and Authorization

1. Introduce the Spring Security module

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copy the code

2. Write Spring Security configuration classes

IO /projects/sp…

Check out the version in our own project to find the corresponding help documentation:

Docs. Spring. IO/spring – secu… # servlet – applications 8.16.4

3. Write basic configuration classes

@EnableWebSecurity // Enable WebSecurity mode
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {}}Copy the code

4. Customize authorization rules for requests

@Override
protected void configure(HttpSecurity http) throws Exception {
   // Customize authorization rules for requests
   // The home page is accessible to all
   http.authorizeRequests().antMatchers("/").permitAll()
  .antMatchers("/level1/**").hasRole("vip1")
  .antMatchers("/level2/**").hasRole("vip2")
  .antMatchers("/level3/**").hasRole("vip3");
}
Copy the code

5, test: found that in addition to the home page can not go! Because we currently do not have a role to log in, because the request requires the role to log in to have the corresponding permissions!

6. Add the following configuration to the configure() method to enable auto-configured login!

// Enable the automatic login function
// /login request comes to the login page
// /login? Error redirects to this to indicate a login failure
http.formLogin();
Copy the code

7, test: found that no permission, will jump to the login page!

8. View the comment information of the login page.

We can define the certification rules and rewrite the configure (AuthenticationManagerBuilder auth) method

// Define authentication rules
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
   // Define it in memory or in JDBC at....
   auth.inMemoryAuthentication()
          .withUser("kuangshen").password("123456").roles("vip2"."vip3")
          .and()
          .withUser("root").password("123456").roles("vip1"."vip2"."vip3")
          .and()
          .withUser("guest").password("123456").roles("vip1"."vip2");
}
Copy the code

9, test, we can use these accounts to log in to test! An error will be reported if found!

There is no PasswordEncoder mapped for the ID “null”

10. The reason is that we need to encrypt the password passed from the front end in some way, otherwise we can’t log in and modify the code

// Define authentication rules
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   // Define it in memory or in JDBC at....
   //Spring Security 5.0 has added multiple encryption methods and changed the password format.
   // We need to modify the configure code in order for our project to log in properly. We're going to have to somehow encrypt the password that comes from the front end
   // Spring Security officially recommends using bcrypt.
   
   auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
          .withUser("zwt").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2"."vip3")
          .and()
          .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1"."vip2"."vip3")
          .and()
          .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1"."vip2");
}
Copy the code

11. Test, find, login is successful, and each role can only access the rules under its own authentication!

Permission control and logout

1. Enable the logout function for automatic configuration

// Customize authorization rules for requests
@Override
protected void configure(HttpSecurity http) throws Exception {
   //....
   // Enable the automatic configuration logout function
      // / Logout The request is logged out
   http.logout();
}
Copy the code

2. We added a logout button in the front, in the navigation bar of index. HTML

3, we can go to test, after the successful login click logout, found that the logout will jump to the login page!

4, however, we want him to log out successfully, can still jump to the home page, how to deal with?

// .logoutSuccessUrl("/"); Logout successful to the home page
http.logout().logoutSuccessUrl("/");
Copy the code

5, test, after logging out, it is found to jump to the home page OK

6. Now we have another requirement: access the page based on permissions

We need to combine some of the functionality in Thymeleaf

SEC: authorize=”isAuthenticated()”: Whether to authenticate login! To display different pages

Maven depends on:

<! -- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
   <groupId>org.thymeleaf.extras</groupId>
   <artifactId>thymeleaf-extras-springsecurity5</artifactId>
   <version>3.04..RELEASE</version>
</dependency>
Copy the code

7. Modify our front-end page

  1. Import namespace

8, restart the test, we can log in to try, login is successful indeed, the page we want to display;

9. If 404 is logged out, it is because it prevents CSRF cross-site request forgery by default. Because of security issues, we can change the request to POST form submission, or turn off CSRF in Spring Security.

http.csrf().disable();// Disable CSRF: Cross-site request forgery. By default, only post can be used to submit the logout request
http.logout().logoutSuccessUrl("/");
Copy the code

Access control and logout done!

Remember that I

1. Enable the Remember me function

// Customize authorization rules for requests
@Override
protected void configure(HttpSecurity http) throws Exception {
//...
   / / remember me
   http.rememberMe();
}
Copy the code

Principle: After successful login, Spring Security sends the cookie to the browser for saving.

In the future, login with this cookie, as long as you pass the check can be free of login.

If you click logout, the cookie will be deleted, the specific principle in the JavaWeb stage have talked about, here will not say more!

Shiro

Apache Shiro is a Security (permissions) framework for Java.

Shiro can do, authentication, authorization, encryption, session management, Web integration, caching and more.

Three core components

Shiro has three core components, Subject, SecurityManager, and Realm

component
Subject User, authentication body The object with which the application code interacts directly is Subject, Subject. The Principals and Credentials are included
SecurityManager Manage all users as security administrator. Is the core of Shiro’s architecture. All interactions with the Subject are delegated to the SecurityManager, the Subject is the facade, and the SecurityManager is the real performer. It is responsible for interacting with Shiro’s other components.
Realm Connect data, which is a field. You can think of a Realm as a DataSource.

Pricipals: indicates the identity of the representative. It can be a username, email, mobile phone number, etc., used to identify a login subject.

Credentials: indicates the Credentials. Common ones are passwords, digital certificates, and so on.

Learn about Shiro in the examples on the website

public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


    public static void main(String[] args) {

      
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

      
        // Get the current user
        Subject currentUser = SecurityUtils.getSubject();

        // Get session from the current user
        Session session = currentUser.getSession();
        // Store values in session
        session.setAttribute("someKey"."aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }

        // Check whether the current user is authenticated
        if(! currentUser.isAuthenticated()) {// Token token is optional
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr"."vespa");
            // Set remember me
            token.setRememberMe(true);
            try {
                currentUser.login(token); // The login operation has been performed
            } catch (UnknownAccountException uae) { // The user name does not exist
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) { // The password is incorrect
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) { // The user is locked
                log.info("The account for username " + token.getPrincipal() + " is locked. " +
                        "Please contact your administrator to unlock it.");
            }
            // ... catch more exceptions here (maybe custom ones specific to your application?
            catch (AuthenticationException ae) { // Authentication is abnormal
                //unexpected condition? error?}}// Get the authentication of the current user
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        // Get the role of the current user
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        // Whether you have coarse-grained (simple) permissions
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring. Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        // Whether to have higher permissions
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        / / logout
        currentUser.logout();
        / / end
        System.exit(0); }}Copy the code

Quick learning

Import dependence

        <! --shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

Copy the code

Write shiro configuration classes

@Configuration
public class ShiroConfig {


    / / shiroFilterBean third step

    / / DefaultWebSecurityManager step 2

    // Creating a Realm object requires the first step of customizing the class
}

Copy the code

Custom Realm

// Custom realms need to inherit AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    / / authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("Authorization executed =>");
        return null;
    }
    / / certification
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("Certification performed");
        return null; }}Copy the code

Continue writing shiro configuration classes

@Configuration
public class ShiroConfig {


    / / ShiroFilterFactoryBean third step
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        // Set the security manager
        filter.setSecurityManager(manager);
        return filter;
    }

    / / DefaultWebSecurityManager step 2
    @Bean(name="manager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        / / associated UserRealm
        manager.setRealm(userRealm);
        return manager;
    }

    // Creating a Realm object requires the first step of customizing the class
    @Bean
    public UserRealm userRealm(a){
        return newUserRealm(); }}Copy the code

Implementing login interception

/ / ShiroFilterFactoryBean third step
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        // Set the security manager
        filter.setSecurityManager(manager);
        // Add shiro built-in filters
        /** * anon: access without authentication * authC: access with authentication * user: access with remember me function * perms: access with permission on a resource * role: access with permission on a role */
        Map<String, String> map = new LinkedHashMap<>();
        map.put("/user/add"."authc");
        map.put("/user/update"."authc");
        filter.setFilterChainDefinitionMap(map);
        // Set the login page
        filter.setLoginUrl("/login");
        return filter;
    }

Copy the code

User authentication

   // User login function
    @PostMapping("/tologin")
    public String tologin(String username, String password, Model model){
        // Get the user
        Subject subject = SecurityUtils.getSubject();
        // Encapsulates user login data and generates a token
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try{
            subject.login(token);// If there is no exception, it is OK
            return "index";// Return to home page
        }catch (UnknownAccountException e){
            model.addAttribute("msg"."User name error");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg"."Password error");
            return "login";
        }

        
        
        
            / / certification
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("Certification performed");
        // The username and password are obtained from the database
        String name="admin";
        String password="123";
        UsernamePasswordToken userName=(UsernamePasswordToken)token;
        if(! userName.getUsername().equals(name)){return null;// Throw an exception
        }
        // Password authentication shiro does
        return new SimpleAuthenticationInfo("",password,"");
    }

Copy the code

Shiro integration mybais

Import dependence

      <! Mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <! - Mysql driver - >
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <! --SpringbootJDBC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

Copy the code

Write the configuration

spring:
  thymeleaf:
    cache: false Turn off caching for the template engine
  ServerTimezone =UTC
  datasource:
    url: jdbc:mysql://localhost:3306/mybatis? serverTimezone=UTC&useSSL=true&useUnicode=true&characterEncoding=utf8
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  type-aliases-package: com.zwt.pojo
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

Copy the code

Write entity classes, write interfaces, write mapping files

Modify custom Realm code

 @Autowired
    private UserMapper userMapper;
    / / certification
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("Certification performed");
        UsernamePasswordToken userName=(UsernamePasswordToken)token;
        // Connect to a real database
        User user = userMapper.queryUserbyName(userName.getUsername());
        if(user==null) {// No user found
            return null;
        }
        Shiro does password encryption
        return new SimpleAuthenticationInfo("",user.getPwd(),"");
    }

Copy the code

Request authorization

Write Shiro filters

1. When setting the /user/add path, you need the [user:add permission

2. If you do not have the access permission, the system redirects to the specified path

 / / ShiroFilterFactoryBean third step
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        // Set the security manager
        filter.setSecurityManager(manager);
        // Add shiro built-in filters
        /** * anon: access without authentication * authC: access with authentication * user: access with remember me function * perms: access with permission on a resource * role: access with permission on a role */
        Map<String, String> map = new LinkedHashMap<>();
       // map.put("/user/add","authc");
        map.put("/user/update"."authc");
        map.put("/user/add"."perms[user:add]"); // Access to this path requires the user:add permission
        filter.setFilterChainDefinitionMap(map);
        // Set the login page
        filter.setLoginUrl("/login");
        filter.setUnauthorizedUrl("/noperms");// Execute this request at no time limit
        return filter;
    }

Copy the code

Authorization:

   / / authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("Authorization executed =>");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("user:add");// Authorize each user

        // Retrieve user information
        Subject subject = SecurityUtils.getSubject();
        User principal = (User)subject.getPrincipal();

        // Set the permission information of user information
        // Dynamic permission setting requires some new database tables, such as permission tables
        //info.addStringPermission(principal.getParms());
        return info;
    }

Copy the code