编程知识 cdmana.com

The use of Shiro is integrated with JWT

One 、shiro introduction

Comparison of two frameworks : Security framework Shiro and SpringSecurity Comparison

understand shiro

What is? Shiro

  1. Apache Shiro It's a Java The safety of the ( jurisdiction ) frame .|
  2. Shiro Can finish , authentication , to grant authorization , encryption , session management ,Web Integrate , Cache, etc .

shiro The function of

1620728018352

Authentication: Identity Authentication 、 Sign in , Verify that the user has an identity ;
Authorization: to grant authorization , That is, permission verification , Verify that an authenticated user has certain privileges , That is, judge whether the user can do anything , Such as : Verify that a user has a role , Or fine-grained verification of whether a user has a right to a resource !
Session Manager: session management , That is, after the user logs in, it is the first session , Before I quit , All of its information is in the conversation ; Conversation can be ordinary JavaSE Environmental Science , It can also be Web Environmental Science ;
Cryptography: encryption , Protect data security , Such as password encryption stored in the database , Instead of plaintext storage ;
# ------------------------------------------------------------------------------------
Web Support: Web Support , Can be easily integrated into Web Environmental Science ;
Caching: cache , Like when the user logs in , Its user information , Owned role 、 Permissions don't have to be checked every time , This can improve efficiency 
Concurrency: Shiro Support concurrent validation for multi-threaded applications , namely , Such as starting another thread in one thread , It can automatically propagate permissions to the past 
Testing: Provide test support ;
Run As: Allows one user to pretend to be another ( If they allow it ) The identity of the access ;
Remember Me: Remember me , This is a very common feature , After one login , You don't have to log in the next time you come 

advantage

  • Simple authentication , Support multiple data sources
  • Simple authorization of roles , Support fine-grained authorization ( Method level ) c、 Support level 1 Cache , To improve application performance
  • Apply to Web And non Web Environment e、 Very simple encryption API
  • Not tied to any frame or container , Can run independently

Shiro The architecture of

image-20210809135229128

image-20210809135412865

function describe
Subject user 、 The main body
Securitymanager Manage all users
Authorizer certifier 、 It's an interface
Authorizer Authorized unit 、 Determine the operation authority
Realm Connect data

Be careful :Shiro They don't maintain users 、_ Maintenance access ; We need to design them ourselves / Provide : Then, it is sent to... Through the corresponding interface Sniro that will do

Dependency and configuration

  • rely on
 <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.7.1</version>
        </dependency>
  • Log configuration (log4j)
#Default Shiro Logging
1og4j.1ogger.org.apache.shiro=INFO

#Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
1og4j.1ogger.org.apache.shiro.cache.ehcache.EhCache=WARN
  • shiro.ini

    ini The document states

  1. [mian] : Define global variables
    • built-in securityManger object
    • When working with built-in objects , stay [main] What's written in it
[main] 
securityManger. attribute = value 
# --
user=com.qd.user
securityManger. Object properties =$user
  1. [users] : Define user name and password
[users]
#  Define the user name as qd666
qd666 = 123456
#  Define the user name as qd666  At the same time admin、role1 Role 
qd666 = 123456, admin,role1
  1. [roles] : Define the role
[roles]
role1= Authority Name 1, Authority Name 2
admin= Authority Name 3, Authority Name 4
# ---  Query of user table , Add permissions 
role1=user:query,user:add
  1. [urls] : Define which built-in urls take effect . stay web Use
[urls]
# url Address = built-in filter Or custom filter
#  Appears when accessing /login Of url We must recognize and support authc Corresponding filter
/login=authc
#  Any of the url There is no need for authentication and other functions 
/**=anon
#  All content must ensure that the user has logged in 
/**=user
# url abc When accessing, the user must have role1 and role2 Role 
/abc=roles["role1,role2"]

image-20210809094719647

Realm structure

image-20210809190452202

Two 、 Function realization

Configure and implement authentication

  1. Authentication

    stay shiro in , Users need to provide principals( identity ) and credentials( prove ) to shiro, So the application can verify the user's identity :

  2. principals

    identity , That is, the identity property of the principal , It could be anything , Such as user name 、 Mailbox, etc , The only can . A body can have more than one principals, But there's only one Primary principals, It's usually a username / password / cell-phone number .

  3. credentials

    prove / By right , Safe values that are known only to the principal , Such as password / Digital certificates are the most common principals and credentials The combination is the user name / It's a password .

The certification process

image-20210809095653237

@Slf4j
public class Certification {
    // Create a security manager factory 
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    // Use the factory to create a security manager 
    SecurityManager securityManager = factory.getInstance();
    // Bind the current security manager to the current thread 
    SecurityUtils.setSecurityManager(securityManager);
    // Get the current user object 
    Subject currentUser = SecurityUtils.getSubject();

    // Get through the current object Session
   // Session session = currentUser.getSession();
   // session.setAttribute("someKey","aValue");
   // String value = (String) session.getAttribute("someKey");

   //  Judge whether the user has passed the authentication 
    if(!currentUser.isAuthenticated()) {
     		// Encapsulate user name and password 
        AuthenticationToken token = new UsernamePasswordToken("lonestarr", "vespa");
        // Set remember me 
        token.setRememberMe(true);
        try {
            // authentication 
            currentUser.login(token);  // Performed login 
        } catch (LockedAccountException lae) {   // User lock 
            log.error(token.getPrincipal() + " is locked");
        } catch (AuthenticationException e) {
            log.error(" Username or password incorrect ");
        }
    }
}

Configure and implement authorization

to grant authorization , Also called access control , That is, who can access which resources in the application ― Such as visiting the page / Edit the data / Page operation, etc. ). Several key objects to understand in Authorization : The main body (Subject)、 resources (Resource)、 jurisdiction (Permission) role (Role).

object describe
The main body Users accessing the app
resources Some pages 、 data
jurisdiction Judge whether the user has the right to operate
role Set of authorities

Authorization process

image-20210809130520720

// Connect to certification        
				// Print its identification body 
        log.info("User ["+currentUser.getPrincipal()+"] logged in successfully.");

        // Role judgment   Uniformly return Boolean values 
        currentUser.hasRole("role1");
        List<String> list= Arrays.asList("role1","role2");
        // Judge whether the current user owns list All characters in the collection 
        currentUser.hasAllRoles(list);
//--------------------------------------------------------
        // Authority judgment 
        currentUser.isPermitted("user:query");
        // Determine whether you have full permissions 
        currentUser.isPermitted("user:query","user:add");
//--------------------------------------------------------
        // Cancellation 
        currentUser.logout();
        // end 
        System.exit(0);

The main methods in authentication and Authorization

 // Get the current user object 
        Subject currentUser = SecurityUtils.getSubject();
 // Get through the current object Session
        Session session = currentUser.getSession();
// Determine whether the user is authenticated 
		currentUser.isAuthenticated()
// Certified 
        currentUser.getPrincipal()
// Determine whether there is this role 
        currentUser.hasRole("schwartz")
// Determine whether you have this permission 
        currentUser.isPermitted("lightsaber:wield")
// Cancellation 
        currentUser.logout();

Be careful : Shiro By default, the built-in IniRealm,IniRealm from ini Read the user's information in the configuration file , But in development, we need to get data from the database , So we need to customize Realm!

Customize Realm Achieve Authentication

Interface to be inherited

Interface describe
CachingRealm Responsible for cache processing
AuthentictionRealm Responsible for certification
AuthorizingRealm Responsible for authorization

Usually customized realm Inherit AuthonizingRealm

  • shiro Configuration class Fixed steps
@Configuration
public class ShiroConfig {
    //1.Realm  resources    Customize userRealm object 
    @Bean
    public Realm userRealm() { return new UserRealm(); }
    //2.securityManager  Perform process control 
    @Bean("securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // relation Realm object 
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //3.ShiroFilterFactoryBean  Request filter 
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        // Set up security manager 
        bean.setSecurityManager(defaultWebSecurityManager);
      // Add filter   Set default request and other operations 
        return bean;
    }
}

​ stay ShiroConfig Add filter

map Medium key The value is ant route 
**    Represents a multi-level path 
*     Represents a unipolar path 
?     Represents a character 
# -----value Is the default filter -------------
anon  :  Access without authentication 
authc :  To access authentication 
user  :  Must have   Remember me   Function can be used 
perms[] :  You can access a resource only if you have permission to it 
role  :  You can only access with the permission of a certain role 
  • *Realm class Inherit AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
    // Authorization method 
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println(" Executed the authorization method ");
     //....
        return null;
    }
  
    // Certification method 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println(" The authentication method is implemented ");
      //......
        return null;
    }
}

Customize Realm Implement Authorization

  1. For properties on some pages / Component authorization
 have access to session solve   
 When you log in, you will currentUser.getPricipal() Put in session in 
 take currentUser.getPricipal() From MyRealm in doGetAuthenticationinfo The authentication method returns SimpleAuthenticationInfo The first property of the object . Front desk access session Just judge 
  1. Control the access rights of the background resource path
  • First visit the page that needs Authorization
// Hard coding , Code redundancy   
		@RequestMapping("/xxx")
    @ResponseBody
    public String unauthorized() {
        Subject subject = SecurityUtils.getSubject();
        if (subject.isPermitted("vip1")) {
            return "ok";
        } else {
            return " This page cannot be accessed without authorization ";
        }
    }

//----- Method 2
// Set unauthorized requests ( page )   This approach requires adding a large number of filter configurations 
bean.setUnauthorizedUrl("/xxx");

//----- Method 3
// annotation    as follows 
// Hard coding , Code redundancy   
    @RequiresPermissions("vip1")
		@RequestMapping("/xxx")
    @ResponseBody
    public String unauthorized() {
       return "ok";
    }
annotation describe
@RequiresAuthentication User login is required
@RequiresGuest Non logged in users can access , You can't access .
@RequiresPermissions Corresponding resource permissions are required
@RequiresRoles You need to have a corresponding role
@RequiresUser You need to complete the user login and complete the remember me function .

tips : When you don't have access , Will throw 500 abnormal AuthorizationException Unified treatment

Simulated Login

Login request It is recommended to define global exception handling

 @RequestMapping("/login")
    public String login(String username, String password) {
        // Get current certification 
        Subject subject = SecurityUtils.getSubject();
        // Encapsulating user login data 
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // Execute login method   Fixed code    If there are no exceptions, it will succeed 
        try {
            subject.login(token);
            return "index";
        } catch (AuthenticationException e) { // The username does not exist 
            model.addAttribute("msg", " Wrong username or password ");
            return "login";
        } 

userRealm

//@Configuration
public class UserRealm extends AuthorizingRealm {

     // Authorization method 
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println(" Executed the authorization method ");
        // Get the current user 
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
        // Definition info, Encapsulate all the roles and permissions of this object 
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // Determine whether the login is a super administrator   Permissions for all 
        if (" Super administrator flag ".equals(currentUser.getPermId())){  
            //1. Get the list of roles and permissions 
            //2. Binding requires roles and permissions 
            info.addRole("");
            info.addStringPermission("");
        }else {
            // Administrators 
            info.addRole("admin");
            info.addStringPermission("*.*");
        }
        return info;
    }

    // Certification method 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println(" The authentication method is implemented ");
        // Get the current user 
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        // Query the real database 
       // User user = userService.queryUserByName(userToken.getUsername());
        // If the query result is empty , It throws an exception 
        if (user == null) {
            return null;
        }
        // Password authentication ,shiro Help us automatically match  , Then complete the certification 
        return new SimpleAuthenticationInfo(user, user.getUserPwd(), "");  // resources , password 
    }
}
        // Set up security manager 
        bean.setSecurityManager(defaultWebSecurityManager);
        Map<String, String> filterMap = new LinkedHashMap<>();
        // Add filter 
        filterMap.put("/user/addUser", "perms[user:add]");
        filterMap.put("/user/*", "authc");
        bean.setFilterChainDefinitionMap(filterMap);
        // Set login request ( page )  Otherwise, an error will be reported 
        bean.setLoginUrl("/login");
        // Set unauthorized requests ( page )
        bean.setUnauthorizedUrl("/xxx");
        return bean;

tips: The more detailed rules are put in the front !

Log out

 //---- Method 1---- request 
  Subject subject = SecurityUtils.getSubject();
  subject.logout();

//---- Method 2---- filter 
 filterMap.put("/user/logout", "logout");

Related articles : Shiro Integrate SpringBoot

3、 ... and 、 Other features

Password encryption

shiro Will get a credentialsMatcher object , To compare passwords . Want to use MD5 Way to encrypt :Md5CredentialsMatcher It's overdue , To use ``HashedCredentialsMatcher` And set the algorithm name .

  1. stay ``securityManager` Set up CredentialsMatcher
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// Set encryption algorithm 
matcher.setHashAlgorithmName("MD5");
// Set the number of encryption 
matcher.setHashIterations(2);
userRealm.setCredentialsMatcher(matcher);
  1. Add salt Change the construction method
 return new SimpleAuthenticationInfo(user, ByteSource.Util.bytes(user.getUserPwd()), ByteSource.Util.bytes("12345"),"");  
  1. matching
public class PasswordEncoder {
    public static String encoder(String password) {
        SimpleHash simpleHash = new SimpleHash("MD5", ByteSource.Util.bytes(password), ByteSource.Util.bytes("12345"), 2);
        return simpleHash.toString();
    }
}

many Realm authentication

Take a chestnut : When logging in to wechat , We can choose to use the mobile phone number 、 You can also select micro signal to log in , So how does this happen ?

  • stay user Add mobile phone number to the table and entity 、 Account number field

  • newly build MobileRealm

@Configuration
public class MobileRealm extends AuthenticatingRealm {

    @Autowired
    UserService userService;
    // authentication 
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println(" The authentication method is implemented ");
        // Get the current user 
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        // Query the real database 
        User user = userService.queryUserByName(userToken.getUsername());
        // If the query result is empty , It throws an exception 
        if (user == null) {
            return null;
        }
        // Password authentication ,shiro Help us automatically match  , Then complete the certification 
        return new SimpleAuthenticationInfo(user, user.getUserPwd(),"");  
    }
}
  • Set up securityManager
// Related objects 
securityManager.setRealms(Arrays.asList(userRealm,mobileRealm));

At this time, you can log in to your account through your mobile phone number In a multiple Realml In our authentication strategy ,AuthenticationStrategy Interface has three implementation classes , understand

image-20210809181845717

Remember me

  • Generally, the front end will provide the remember me option on the page
  • Realization
 if (subject.isAuthenticated() && subject.isRemembered()) {
      subject.login(token);
      token.setRememberMe(true);
 }   
  • Remember that my function corresponds to... By default ``user` filter

  • Set up securityManager

CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
// prevent xss Read cookie
simpleCookie.setHttpOnly(true);
// Remember me cookie entry-into-force time 30 God  , Unit second 
simpleCookie.setMaxAge(2592000);
rememberMeManager.setCookie(simpleCookie);
	securityManager.setRememberMeManager(rememberMeManager);

In microservices and distributed involves cookie Sharing issues Wait, wait, wait ...

Authentication cache

Each time we log in, we can get... From the cache first , If you are not querying from the database , Improve performance

  • stay securityManager Open the cache
// cache 
AbstractCacheManager cacheManager = new MemoryConstrainedCacheManager();
securityManager.setCacheManager(cacheManager);

Remember me , Session management and authentication caching , You can extend the corresponding manager How to interface , Realize your own flexible expansion , For example, share information to redis.

combination Thylemeaf

  1. Import dependence
        <!-- Import shiro And Thymeleaf Integrated package -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
  1. To configure ShiroConfig
    // Integrate ShiroDialect: For integration shiro And thymeleaf
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }
  1. Specific use : shiro springsecurity thymeleaf Tag usage and namespace
  2. Notes explain : Annotations use

Four 、 Integrate JWT

thus , Finally, I came to the place I really wanted to write , In actual development , Interface design usually takes into account security and permission design ,JWT Of token The mechanism also does not consume server memory 、 utilize JWT It can also realize single sign on and other functions . Let's simply integrate the use of the two

JSON Web Token(JWT) It's a very light standard . This specification allows us to use JWT Deliver secure information between users and servers . We use a certain amount of coding to generate Token, And in Token Add some non sensitive information to , Pass it on .

When the user logs in successfully, a certificate will be issued to the user token,token from jwt Make . Back up token Before the expiration date , User access depends on token. If token Expired or tampered with , The user is required to log in again . stay token Is valid , All the interfaces that users need permission to access must go through shiro Two core annotations for RequiresRoles as well as RequiresPermissions Validation of the .

  1. Import dependence
        <!-- To configure shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.7.1</version>
        </dependency>
        <!-- To configure JWT-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
  1. JWTUtil
    Generate token And verification token Appoint token Expiration time EXPIRE_TIME And the signature key SECRET
public class JwtUtil {
    // secret key 
    private static final String SECRET = "!Q464rwr(&*)6%$#^%&JNJ46da";

    /**
     *  Generate token
     * @param map: Information to encrypt 
     * @return  token 
     */
    public static String sign(Map<String, String> map) {
        // Set expiration time   Default 3 God 
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE, 3);
        JWTCreator.Builder builder = JWT.create();
        //payload
        map.forEach((k, v) -> {
            builder.withClaim(k, v);
        });
        // Specify token expiration time and encryption 
        String token = builder.withExpiresAt(instance.getTime())
                .sign(Algorithm.HMAC256(SECRET));
        return token;
    }

    /**
     *  verification token Legitimacy / obtain token All information 
     * @param token
     * @return verify
     */
    public static DecodedJWT verify(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
    }

    /**
     *  obtain token Single message 
     * @param token
     * @param s:key
     * @return value
     */
    public static String getInfo(String token, String s) {
        DecodedJWT verify = verify(token);
        return verify.getClaim(s).asString();
    }
}
  1. JWTFilter
    on top , We use shiro The default privilege block Filter, And because JWT Integration of , We need to customize our own filters JWTFilter
public class JWTFilter extends BasicHttpAuthenticationFilter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     *  If a  token, On the other hand  token  Inspection , Otherwise, directly through 
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        // Determine whether the request header is attached  "Token"
        if (isLoginAttempt(request, response)) {
            // If there is , entering  executeLogin  Method execution login , Check  token  Whether it is right 
            try {
                executeLogin(request, response);
                return true;
            } catch (Exception e) {
                System.out.println("token  error ");
                //token  error     Illegal access 
                throw new RuntimeException("Unauthorized access!");
               // responseError(response, e.getMessage());
            }
        }
        // If the request header does not exist  Token, It may be a login operation or a visitor status visit , No need to check  token, Go straight back to  true
        return true;
    }

    /**
     *  Determine whether the user wants to log in .
     *  testing  header  Does it contain  Token  Field 
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String token = req.getHeader("Authorization");
        return token != null;
    }

    /**
     *  Perform the login operation 
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Authorization");
        JWTToken jwtToken = new JWTToken(token);
        //  Submit to realm Log in , If it's wrong, it throws an exception and gets caught 
        getSubject(request, response).login(jwtToken);
        //  If no exception is thrown, the login is successful , return true
        return true;
    }

    /**
     *  Support for cross domain 
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        //  Cross domain will first send a option request , Here we give option Request direct return to normal state 
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     *  Jump illegal request to  /unauthorized/**
     */
    private void responseError(ServletResponse response, String message) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            // Set encoding , Otherwise, Chinese characters will become empty strings during redirection 
            message = URLEncoder.encode(message, "UTF-8");
            httpServletResponse.sendRedirect("/unauthorized/" + message);
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }
}

  1. JWTToken
    Realization AuthenticationToken Rewrite two methods
public class JWTToken implements AuthenticationToken {
    private String token;

    public JWTToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}
  1. ShiroConfig
    Set the custom Filter, Let the request pass through the filter
@Configuration
public class ShiroConfig {
    /**
     *  Go ahead  filter , then  filter  If a request header is detected  token, Then use  token  Go to  login, go  Realm  To verify 
     */
    @Bean
    public ShiroFilterFactoryBean factory(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //  Add your own filter and call it jwt
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        // Set up our custom JWT filter 
        filterMap.put("jwt", new JWTFilter());
        factoryBean.setFilters(filterMap);
        factoryBean.setSecurityManager(securityManager);
        //  Set the time limit for jump  url;
        factoryBean.setUnauthorizedUrl("/unauthorized/ No authority ");
        Map<String, String> filterRuleMap = new HashMap<>();
        //  All requests through our own JWT Filter
        filterRuleMap.put("/**", "jwt");
        //  visit  /unauthorized/**  Not through JWTFilter
        filterRuleMap.put("/unauthorized/**", "anon");
        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

    /**
     *  Inject  securityManager
     */
    @Bean
    public SecurityManager securityManager(MyRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //  Set customization  realm.
        securityManager.setRealm(myRealm);
        // close shiro Self contained session, See the document for details 
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    /**
     *  Add annotation support 
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        //  Compulsory use cglib, Prevent duplicate agents and problems that may cause agent errors 
        // https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
}
  1. Realm class
    Customize our Realm Realize authentication and authorization
@Component
public class MyRealm extends AuthorizingRealm {
    private final UserMapper userMapper;
    
    @Autowired
    public CustomRealm(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    /**
     *  This method must be overridden , Otherwise, it will report a mistake 
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     *  By default, this method is used to verify whether the user name is correct , Just throw an exception .
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("———— Authentication method ————");
        String token = (String) authenticationToken.getCredentials();
        //  Decrypt to obtain username, For comparison with database 
        String username = JWTUtil.getInfo(token,"username");
        if (username == null) {
            throw new AuthenticationException("token Authentication failed !");
        }
        User user = userMapper.queryUserByName(username);
        if (user.getPassWord() == null) {
            throw new AuthenticationException(" The user does not exist !");
        }
        return new SimpleAuthenticationInfo(token, token, "MyRealm");
    }

    /**
     *  This method is called only when the user permission needs to be detected , for example checkRole,checkPermission And so on. 
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("———— Permission authentication ————");
        String token= principals.toString();
        String username = JWTUtil.getInfo(token,"username");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // Get the user role 
        User user = userMapper.queryUserByName(username);
        // Each role has default permissions 
        int perm = user.getPerm();
        // Each user can set new permissions 
        //  String permission = userMapper.getPermission(username);
        Set<String> roleSet = new HashSet<>();
        Set<String> permissionSet = new HashSet<>();
        // Need to put  role, permission  Package to  Set  As  info.setRoles(), info.setStringPermissions()  Parameters of 
        roleSet.add(perm + "");
        //   permissionSet.add(rolePermission);
        //  permissionSet.add(permission);
        // Set the roles and permissions that the user has 
        info.setRoles(roleSet);
        info.setStringPermissions(permissionSet);
        return info;
    }
}
  1. Controller
    @PostMapping("/login")
    public ResultMap login(@RequestParam("username") String account,
                           @RequestParam("password") String password) {
        User user = userMapper.queryUserByName(account);
        if (user == null || !user.getPassWord().equals(password)) {
            throw new RuntimeException(" Wrong user name or password ");
        }
        Map<String, String> map = new HashMap<>();
        map.put("username", user.getAccount());
        return R.ok(JwtUtil.sign(map), " Login successful ");
    }


    /**
     *  Have  1、2、3  Users of the role can access the following page 
     */
    @GetMapping("/getMessage")
    @RequiresRoles(logical = Logical.OR, value = {"1", "2", "3"})
    public R getMessage() {
       return R.ok("", " Successful access to information ");
    }

Running results :
image-20210812145241493
No token In the case of an interview :
image-20210812145435590
Plus the situation :
image-20210812150602147


The End~~

版权声明
本文为[Anterior acridine]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/10/20211002145410457h.html

Scroll to Top