This is the fourth day of my participation in the August Challenge. For details, see:August is more challenging

1. An overview of the

1.1 SpringBoot

This is not much to say, can see this tutorial, estimated all can say to be proficient in the use of SpringBoot

1.2 Shiro

A security framework, but not just a security framework. It can achieve a variety of functions. It’s not just limited to the Web tier. In the domestic market share is higher than SpringSecurity, is the most used security framework

Users can be authenticated and authorized. Much simpler than SpringSecurity.

1.3 Jwt

It is a technique that can be used to authenticate between client and server, replacing the insecure use of Session authentication

Why not Session?

After the login, the client and server each store a corresponding SessionId. Each time the client initiates a request, the client carries this SessionId for comparison

  1. Sessions are too expensive for the server when the number of user requests is high
  2. Session is not a good way to cluster servers (i.e., you have to access the original server to get the corresponding Session ID)

It uses a token technique

The Jwt string is divided into three parts

  1. Header

    Store two variables

    1. Secret key (can be used for comparison)
    2. Algorithm (which encrypts the Header and payload as Signature)
  2. payload

    Store a lot of things, the basic information is as follows

    1. The signer, the user to which the token belongs. The general isuserId
    2. Creation time, when was this token created
    3. Expiration time, when does this token expire
    4. A unique identifier, which can generally be generated using an algorithm
  3. Signature

    This is generated by the above two encryption algorithms in the Header. It is used to compare information and prevent tampering with the Header and payload

Then the information of these three parts is encrypted to generate a string of JwtToken and sent to the client. The client saves it locally. When the client initiates the request, it carries it to the server (it can be in the cookie, it can be in the header, it can be in the localStorage) and authenticates it on the server

Well, no more nonsense, the next start of actual combat, actual combat is divided into the following parts

  1. SpringBootintegrationShiro
  2. SpringBootintegrationJwt
  3. SpringBoot+Shiro+Jwt
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
Copy the code

2. SpringBoot integrates Shiro

Two ways:

  1. Write the SSM integration configuration in springBoot using Java code
  2. Use the official start

2.1 Integrating springBoot with Start

pom.xml

<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> The < version > 1.4.0 < / version > < / dependency > <! -- shiro-spring-boot-starter-->Copy the code

application.properties

Shiro. UnauthorizedUrl=" XXX "# Redirect page that does not pass the authorizationCopy the code

Create shiroconfig.java for some simple configuration

@Configuration public class SpringShiroConfig { @Bean public Realm customRealm() { return new CustomRealm(); } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm()); // Disable the ShiroDAO function DefaultSubjectDAO subjectDAO = New DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); / / no need to Shiro things to any places in the Session (including Http Session) defaultSessionStorageEvaluator. SetSessionStorageEnabled (false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); securityManager.setSubjectDAO(subjectDAO); return securityManager; } @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition(); AddPathDefinition ("/login", "anon"); // Which requests can be accessed anonymously by chain.addPathDefinition("/login", "anon"); AddPathDefinition ("/notLogin", "anon"); AddPathDefinition ("/403", "anon"); // Interface chain.addPathDefinition("/403", "anon"); AddPathDefinition ("/**", "authc"); addPathDefinition("/**", "authc"); return chain; } / / Shiro and Spring AOP integration of special setup @ Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator () { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } // Turn off the ShiroDao functionCopy the code

Create a custom Realm

public class CustomRealm extends AuthorizingRealm { private static final Set<String> tomRoleNameSet = new HashSet<>(); private static final Set<String> tomPermissionNameSet = new HashSet<>(); private static final Set<String> jerryRoleNameSet = new HashSet<>(); private static final Set<String> jerryPermissionNameSet = new HashSet<>(); static { tomRoleNameSet.add("admin"); jerryRoleNameSet.add("user"); tomPermissionNameSet.add("user:insert"); tomPermissionNameSet.add("user:update"); tomPermissionNameSet.add("user:delete"); tomPermissionNameSet.add("user:query"); jerryPermissionNameSet.add("user:query"); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); if (username.equals("tom")) { info.addRoles(tomRoleNameSet); info.addStringPermissions(tomPermissionNameSet); } else if (username.equals("jerry")) { info.addRoles(jerryRoleNameSet); info.addStringPermissions(jerryPermissionNameSet); } return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); If (username == null) throw new UnknownAccountException(" username cannot be null "); SimpleAuthenticationInfo info = null; if (username.equals("tom")) return new SimpleAuthenticationInfo("tom", "123", CustomRealm.class.getName()); else if (username.equals("jerry")) return new SimpleAuthenticationInfo("jerry", "123", CustomRealm.class.getName()); else return null; }}Copy the code

2.2 Not using starter

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

Write Shiro’s configuration class: ShiroConfig

To rewrite Shiro’s configuration information (spring-shiro.xml and spring-web.xml) in Java code configuration:

@Configuration public class ShiroConfig { @Bean public Realm realm() { return new CustomRealm(); } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } @Bean public ShiroFilterFactoryBean shirFilter() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); shiroFilterFactoryBean.setLoginUrl("/loginPage"); shiroFilterFactoryBean.setUnauthorizedUrl("/403"); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/loginPage", "anon"); filterChainDefinitionMap.put("/403", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/hello", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /* ################################################################# */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); / / forced to specify annotations of the underlying implementation using defaultAdvisorAutoProxyCreator. Additional protocol setProxyTargetClass (true); return defaultAdvisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; }}Copy the code

Writing the Controller

Same with Shiro and SSM integration. slightly

Write the Thymeleaf page

slightly

3. SpringBoot integrates Jwt

3.1 depend on

Springboot 2. Java-jwt -- core dependency 3. JJWT -- auxiliary help module for Java versionCopy the code

3.2 code

  1. Create JwtUtil

    package cn.coderymy.utils;
    
    import java.util.*;
    import com.auth0.jwt.*;
    import com.auth0.jwt.algorithms.Algorithm;
    import io.jsonwebtoken.*;
    import org.apache.commons.codec.binary.Base64;
    
    import java.util.*;
    
    
    public class JwtUtil {
    
        // The generated signature is the secret key used
        private final String base64EncodedSecretKey;
    
        // The encryption algorithm used to generate the signature
        private final SignatureAlgorithm signatureAlgorithm;
    
        public JwtUtil(String secretKey, SignatureAlgorithm signatureAlgorithm) {
            this.base64EncodedSecretKey = Base64.encodeBase64String(secretKey.getBytes());
            this.signatureAlgorithm = signatureAlgorithm;
        }
    
        /** * Generate JWT Token string **@paramIss Name of issuer *@paramTtlMillis JWT expiration time *@paramClaims additional information added to the Netherlands section. * For example, you can add information such as user name, user ID, user (before encryption) password */
        public String encode(String iss, long ttlMillis, Map<String, Object> claims) {
            if (claims == null) {
                claims = new HashMap<>();
            }
    
            // Issue time (IAT) : one of the standard fields of the load section
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
    
            // You can add standard and private declarations for the payload
            JwtBuilder builder = Jwts.builder()
                    // Non-standard fields/additional fields in the load section, usually written before the standard fields.
                    .setClaims(claims)
                    // JWT ID (JTI) : one of the standard fields of the load part, the unique identifier of JWT. Although it is not required, try to ensure its uniqueness.
                    .setId(UUID.randomUUID().toString())
                    // Issue time (IAT) : one of the standard fields in the load section, representing the generation time of this JWT.
                    .setIssuedAt(now)
                    // Signer (ISS) : one of the standard fields of the load section, representing the owner of this JWT. Usually, the content is representative of the user, such as username and userID.
                    .setSubject(iss)
                    // Set the algorithm and key for generating the signature
                    .signWith(signatureAlgorithm, base64EncodedSecretKey);
    
            if (ttlMillis >= 0) {
                long expMillis = nowMillis + ttlMillis;
                Date exp = new Date(expMillis);
                // Expiration time (EXP) : one of the standard fields in the load section, representing the validity of this JWT.
                builder.setExpiration(exp);
            }
    
            return builder.compact();
        }
    
    
        /** * JWT Token consists of three parts: head load part and signature part. The signature part is generated by the encryption algorithm and cannot be decrypted reversely. * The header and load sections are generated by the Base64 encoding algorithm and can be reverse coded back. * This is why you should not put sensitive data in JWT tokens. * *@paramJwtToken Encrypted token *@returnClaims returns key value pairs */ for the load part
        public Claims decode(String jwtToken) {
    
            / / get DefaultJwtParser
            return Jwts.parser()
                    // Set the key for the signature
                    .setSigningKey(base64EncodedSecretKey)
                    // Set the JWT to parse
                    .parseClaimsJws(jwtToken)
                    .getBody();
        }
    
    
        /** * Verify token * You can use the official verification or custom verification rules. For example, the token carries a password, which is encrypted and compared with the encrypted password in the database. * *@paramJwtToken Indicates the JWT Token */ to be verified
        public boolean isVerify(String jwtToken) {
            Algorithm algorithm = null;
    
            switch (signatureAlgorithm) {
                case HS256:
                    algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodedSecretKey));
                    break;
                default:
                    throw new RuntimeException("This algorithm is not supported");
            }
    
            JWTVerifier verifier = JWT.require(algorithm).build();
            verifier.verify(jwtToken);  // If the check fails, an exception is thrown
    
    
            /* // Get DefaultJwtParser Claims Claims = decode(jwtToken); if (claims.get("password").equals(user.get("password"))) { return true; } * /
    
            return true;
        }
    
        public static void main(String[] args) {
            JwtUtil util = new JwtUtil("tom", SignatureAlgorithm.HS256);
    
            Map<String, Object> map = new HashMap<>();
            map.put("username"."tom");
            map.put("password"."123456");
            map.put("age".20);
    
            String jwtToken = util.encode("tom".30000, map);
    
            System.out.println(jwtToken);
            /* util.isVerify(jwtToken); System. The out. Println (" legal "); * /
    
            util.decode(jwtToken).entrySet().forEach((entry) -> {
                System.out.println(entry.getKey() + ":"+ entry.getValue()); }); }}Copy the code

    Resolution:

    1. Several values need to be passed in when creating a JwtUtil object
      1. This user is used to generate the secret key
      2. This encryption algorithm is used to generate JWT encryption
    2. How to get user information from JWT data (decode())
    3. A method to determine whether JWT exists or is out of date
    4. Finally, the test method
  2. Create a Controller

    1. Login Controller
      1. Obtain the username and password and verify them with the database. If the verification succeeds, go to the next step
      2. You create a JwtUtil object, passing in username and the encryption algorithm you want to use
      3. Creates a map object that needs to add some basic information to the payload
      4. Create a MAP of the JWT data, passing in the username, save time, and basic information
    2. Check the Controller
      1. Gets the Jwt data passed in the foreground
      2. useJWTUtilIn theisVerifyVerify that the JWT data is valid

4. SpringBoot+Shiro+Jwt

  1. Because you need to set Shiro’s SecurityManager, you cannot use Shro-spring-boot-starter to integrate with SpringBoot. Only Spring-Shiro can be used

    <! -- Automatically import shro-core and Shro-web -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.1</version>
    </dependency>
    Copy the code
  2. Because of the need to implement a stateless Web, Shiro’s Session feature is not available, but it should be turned off

    public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory {
    
        @Override
        public Subject createSubject(SubjectContext context) {
            // Do not create session
            context.setSessionCreationEnabled(false);
            return super.createSubject(context); }}Copy the code

    This will throw an exception if the getSession() method is called

4.1 process

  1. The user requests, without the token, to throw an exception/return no login in the JwtFilter and let it log in
  2. User request, carry token, get JWT from JwtFilter, encapsulated as JwtToken object. JwtRealm is then used for authentication
  3. Authentication is performed in JwtRealm to determine whether the token is valid, i.e
Execution process: 1. The client initiates a request, shiro filters take effect, and checks whether the request is a Login or logout request<br/>If yes, execute the request directly<br/>If not, enter JwtFilter2. JwtFilter execution process 1. If no, throw exception 2. If no, throw exception 2. The obtained JWT string is encapsulated in the created JwtToken, and the verification is performed using the login() method using Subject. This method calls the created JwtRealm 3. Execute the authentication method in JwtRealm, using`jwtUtil.isVerify(jwt)`4. Return true to keep the base runningCopy the code

4.2 Quick Start

0. JwtDeafultSubjectFactory

package cn.coderymy.shiro;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;

public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory {

    @Override
    public Subject createSubject(SubjectContext context) {
        // Do not create session
        context.setSessionCreationEnabled(false);
        return super.createSubject(context); }}Copy the code

1. Create JwtUtil

This is usually a fixed notation with a lot of comments

package cn.coderymy.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.binary.Base64;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/* * In general, there are three methods in the utility class * to get the JwtToken, to get the information encapsulated in the JwtToken, to determine whether the JwtToken exists * 1.encode (), parameter = issuer, existence time, some other information =. The return value is the string * 2.decode () for JwtToken, and the argument is =JwtToken=. The return value is the load part of the key-value pair * 3.isverify (), the argument is =JwtToken=. The return value is whether the JwtToken exists * */
public class JwtUtil {
    // Create a default secret key and algorithm for the constructor with no arguments
    private static final String defaultbase64EncodedSecretKey = "badbabe";
    private static final SignatureAlgorithm defaultsignatureAlgorithm = SignatureAlgorithm.HS256;

    public JwtUtil(a) {
        this(defaultbase64EncodedSecretKey, defaultsignatureAlgorithm);
    }

    private final String base64EncodedSecretKey;
    private final SignatureAlgorithm signatureAlgorithm;

    public JwtUtil(String secretKey, SignatureAlgorithm signatureAlgorithm) {
        this.base64EncodedSecretKey = Base64.encodeBase64String(secretKey.getBytes());
        this.signatureAlgorithm = signatureAlgorithm;
    }

    /* * This is where the JWT string is generated * the JWT string has three parts * 1. header * - the type of the current string, usually "JWT" * - which encryption algorithm, "HS256" or some other encryption algorithm * so it is usually fixed, Payload * iAT: the time when the JWT was generated * jti: the unique identifier of the JWT * iss: the person who signed the JWT, which is usually username or userId * exp: Expiration time * * */
    public String encode(String iss, long ttlMillis, Map<String, Object> claims) {
        // ISS signer, ttlMillis time of life, claims refers to some non-private information that is still wanted to be stored in JWT
        if (claims == null) {
            claims = new HashMap<>();
        }
        long nowMillis = System.currentTimeMillis();

        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setId(UUID.randomUUID().toString())//2. This is the unique identifier of the JWT. It is usually set to unique
                .setIssuedAt(new Date(nowMillis))//1. This is the iAT generated by converting the current system time in milliseconds
                .setSubject(iss)//3. The name of the user to whom the JWT is issued
                .signWith(signatureAlgorithm, base64EncodedSecretKey);// This is where the algorithm and key used to generate JWT are generated
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);//4. Expiration time, which is also generated in milliseconds, is generated using the current time + the duration passed in earlier
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    // Pass in jwtToken to generate the corresponding fields such as username and password. Claim is a map
    // Get all the key pairs in the load section
    public Claims decode(String jwtToken) {

        / / get DefaultJwtParser
        return Jwts.parser()
                // Set the key for the signature
                .setSigningKey(base64EncodedSecretKey)
                // Set the JWT to parse
                .parseClaimsJws(jwtToken)
                .getBody();
    }

    // Check whether the jwtToken is valid
    public boolean isVerify(String jwtToken) {
        // This is the official validation rule, here only write a "validation algorithm", you can add your own
        Algorithm algorithm = null;
        switch (signatureAlgorithm) {
            case HS256:
                algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodedSecretKey));
                break;
            default:
                throw new RuntimeException("This algorithm is not supported");
        }
        JWTVerifier verifier = JWT.require(algorithm).build();
        verifier.verify(jwtToken);  // If the check fails, an exception is thrown
        1. The head and load parts have not been tampered with. 2. It's not expired
        return true;
    }

    public static void main(String[] args) {
        JwtUtil util = new JwtUtil("tom", SignatureAlgorithm.HS256);
        // Use Tom as the secret key and HS256 encryption
        Map<String, Object> map = new HashMap<>();
        map.put("username"."tom");
        map.put("password"."123456");
        map.put("age".20);

        String jwtToken = util.encode("tom".30000, map);

        System.out.println(jwtToken);
        util.decode(jwtToken).entrySet().forEach((entry) -> {
            System.out.println(entry.getKey() + ":"+ entry.getValue()); }); }}Copy the code

2. Create JwtFilter

This means adding one more filter to Shiro’s interceptor, which you will need to register in your configuration file later

package cn.coderymy.filter;

import cn.coderymy.shiro.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/* * Customize a Filter that blocks all requests to check whether the Token is carried * isAccessAllowed() Check whether the Token is valid * onAccessDenied() is used to log in without JwtToken. Access allowed after login succeeds, access denied after login fails * */
@Slf4j
public class JwtFilter extends AccessControlFilter {
    /* * 1. Return true, shiro allows access to url * 2. If false is returned, Shiro will decide whether to allow access to the URL * */ based on the return value of onAccessDenied
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        log.warn("The isAccessAllowed method is called");
        // Let it always return false to use the onAccessDenied() method
        return false;
    }

    /** * Returns true indicating that the login passed */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        log.warn("OnAccessDenied method is called");
        // This place is consistent with the front-end convention that requires the front-end to place the jwtToken in the Header part of the request

        // So in the future, we need to put an Authorization in the Header, and the value is the corresponding Token
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        log.info("Request Header contains jwtToken {}", jwt);
        JwtToken jwtToken = new JwtToken(jwt);
        /* *
        try {
            // Delegate realm to authenticate the login
            // So this is where JwtRealm authentication is finally called
            getSubject(servletRequest, servletResponse).login(jwtToken);
            // That is, subject.login(token)
        } catch (Exception e) {
            e.printStackTrace();
            onLoginFail(servletResponse);
            // Call the following method to return an error message to the client
            return false;
        }

        return true;
        // If no exception is thrown in the execution method, the login is successful
    }

    // If the login fails, 401 status code is returned by default
    private void onLoginFail(ServletResponse response) throws IOException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpResponse.getWriter().write("login error"); }}Copy the code

3. Create JwtToken

It encapsulates the JWT string that needs to be passed

package cn.coderymy.shiro;

import org.apache.shiro.authc.AuthenticationToken;

// This is similar to UsernamePasswordToken
public class JwtToken implements AuthenticationToken {

    private String jwt;

    public JwtToken(String jwt) {
        this.jwt = jwt;
    }

    @Override// Similar to the user name
    public Object getPrincipal(a) {
        return jwt;
    }

    @Override// Similar to password
    public Object getCredentials(a) {
        return jwt;
    }
    // Return JWT
}
Copy the code

4. JwtRealm

Creates a Realm for an authentication mode that determines whether JWT is valid

package cn.coderymy.realm;

import cn.coderymy.shiro.JwtToken;
import cn.coderymy.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
@Slf4j
public class JwtRealm extends AuthorizingRealm {
    This Realm is dedicated to validating jwtTokens * not responsible for validating other tokens (UsernamePasswordToken) * */
    @Override
    public boolean supports(AuthenticationToken token) {
        // This token is the jwtToken passed in from the filter
        return token instanceof JwtToken;
    }

    / / authorization
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    / / certification
    // This token is the jwtToken passed in from the filter
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String jwt = (String) token.getPrincipal();
        if (jwt == null) {
            throw new NullPointerException("JwtToken is not allowed to be empty");
        }
        / / determine
        JwtUtil jwtUtil = new JwtUtil();
        if(! jwtUtil.isVerify(jwt)) {throw new UnknownAccountException();
        }
        // Verify that the user is real
        String username = (String) jwtUtil.decode(jwt).get("username");// Check whether username exists in the database
        log.info("Login using token"+username);
        return new SimpleAuthenticationInfo(jwt,jwt,"JwtRealm");
        // This returns something like an account password, but the jwtToken is a JWT string. You also need a class name for the Realm}}Copy the code

5. ShiroConfig

Configure some information

  1. Since sessions are not applicable, the custom Subject method is called by default in case of an error that would result from calling the getSession() method
  2. Some modifications, close SHiroDao, etc
  3. Registered JwtFilter
package cn.coderymy.config;

import cn.coderymy.filter.JwtFilter;
import cn.coderymy.realm.JwtRealm;
import cn.coderymy.shiro.JwtDefaultSubjectFactory;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

//springBoot integration JWT implementation has three different aspects, corresponding to the following ABC
@Configuration
public class ShiroConfig {
    /* * a. Tell Shiro not to use the default DefaultSubject to create objects, because Session * */ cannot be created
    @Bean
    public SubjectFactory subjectFactory(a) {
        return new JwtDefaultSubjectFactory();
    }

    @Bean
    public Realm realm(a) {
        return new JwtRealm();
    }

    @Bean
    public DefaultWebSecurityManager securityManager(a) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        /* * b * */
        // Disable the ShiroDAO function
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        // There is no need to store anything from Shiro Session anywhere (including Http Session)
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        // Disallow the Subject's getSession method
        securityManager.setSubjectFactory(subjectFactory());
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(a) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager());
        shiroFilter.setLoginUrl("/unauthenticated");
        shiroFilter.setUnauthorizedUrl("/unauthorized");
        /* * c. Add a JWT Filter and register the jwtFilter to Shiro's Filter. * Specify that all requests except login and logout go through jwtFilter * */
        Map<String, Filter> filterMap = new HashMap<>();
        // The other two filters can be left unset
        filterMap.put("anon".new AnonymousFilter());
        filterMap.put("jwt".new JwtFilter());
        filterMap.put("logout".new LogoutFilter());
        shiroFilter.setFilters(filterMap);

        / / the interceptor
        Map<String, String> filterRuleMap = new LinkedHashMap<>();
        filterRuleMap.put("/login"."anon");
        filterRuleMap.put("/logout"."logout");
        filterRuleMap.put("/ * *"."jwt");
        shiroFilter.setFilterChainDefinitionMap(filterRuleMap);

        returnshiroFilter; }}Copy the code

Test 6.

package cn.coderymy.controller;

import cn.coderymy.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.HashMap;
import java.util.Map;
@Slf4j
@Controller
public class LoginController {

    @RequestMapping("/login")
    public ResponseEntity<Map<String, String>> login(String username, String password) {
        log.info("username:{},password:{}",username,password);
        Map<String, String> map = new HashMap<>();
        if (!"tom".equals(username) || !"123".equals(password)) {
            map.put("msg"."Incorrect user name or password");
            return ResponseEntity.ok(map);
        }
        JwtUtil jwtUtil = new JwtUtil();
        Map<String, Object> chaim = new HashMap<>();
        chaim.put("username", username);
        String jwtToken = jwtUtil.encode(username, 5 * 60 * 1000, chaim);
        map.put("msg"."Login successful");
        map.put("token", jwtToken);
        return ResponseEntity.ok(map);
    }
    @RequestMapping("/testdemo")
    public ResponseEntity<String> testdemo(a) {
        return ResponseEntity.ok("I love egg-fried rice."); }}Copy the code

4.3 Authorization Information

In the authorization section of JwtRealm, you can use JwtUtil.decode(JWT).get(“username”) to obtain username, use username to find the corresponding permission in the database, and then assign the permission to the user