login authentication

one. Apache Shiro is a powerful and easy-to-use Java security framework that provides authentication, authorization, encryption , and session management.

 

two. Shiro provides APIs to protect applications to solve the following problems:

  Authentication - user identification, often referred to as user "login";

  Authorization - access control;

  Password encryption - protect or hide data from prying eyes;

  Session management - per-user related time-sensitive state.

 

three. Core Concepts : Subject, SecurityManager and Realms

 

\

Subject  

"Current operating user". However, in Shiro, the concept of Subject does not only refer to people, but can also be third-party processes, background accounts (DaemonAccount), or other similar things. It simply means "what is currently interacting with the software". But for most purposes and uses, you can think of it as Shiro's concept of "user".

Subject represents the security operations of the current user, and SecurityManager manages the security operations of all users.

SecurityManager

  It is the core of Shiro framework, typical Facade mode, Shiro manages internal component instances through SecurityManager, and provides various services for security management through it.

Realms

  Realm acts as a "bridge" or "connector" between Shiro and application security data. That said, when actually interacting with security-related data like user accounts, performing authentication (login) and authorization (access control), Shiro looks a lot from the Realm configured by the app.

  In this sense, Realm is essentially a security-related DAO: it encapsulates the connection details of a data source and provides relevant data to Shiro when needed. When configuring Shiro, you must specify at least one Realm for authentication and/or authorization. It is possible to configure multiple Realms, but at least one is required.

Certification process:

 

\

 

 

1. After the application builds an AuthenticationToken instance of end-user authentication information, it calls the Subject.login method.

2. Sbuject will delegate the securityManager instance set by the application to call the securityManager.login(token) method.

3. After the SecurityManager receives the token (token) information, it will delegate an instance of the built-in Authenticator (usually an instance of the ModularRealmAuthenticator class) to call authenticator.authenticate(token).ModularRealmAuthenticator will verify the setting of one or more Realms during the authentication process. The instance is adapted, which actually provides a pluggable authentication mechanism for Shiro.

4. If multiple Realms are configured in the application, ModularRealmAuthenticator will perform the multi-Realm authentication process according to the configured AuthenticationStrategy (authentication strategy). After the Realm is called, the AuthenticationStrategy will respond to the result of each Realm.

Note: If only one Realm is configured in the application, the Realm will be called directly without configuring the authentication policy.

5. Realm will call getAuthenticationInfo(token); the getAuthenticationInfo method is the actual authentication process. We write our custom authentication process by overriding Realm's doGetAuthenticationInfo method.

 

Let's combine an example to understand the above concepts (combined with SpringMVC):

1. Add Shiro Filter to Web.xml (all .do requests go through the filter)

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml,classpath:spring-shiro.xml</param-value>
</context-param>
<!-- apache shiro permissions-->
<!-- The Shiro main filter itself is very powerful, its power is that it supports the execution of any custom filter based on URL path expressions-->
<!-- Shiro Filter -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>

2.spring-m vc .xml (integrated with springMVC)

<!-- Turn on Shiro's annotations to implement method-level permission checks for Controller (such as @RequiresRoles, @RequiresPermissions), and use SpringAOP to scan classes annotated with Shiro, and perform security logic verification when necessary -->
<!-- Configure the following two beans to achieve this function, which should be placed in spring-mvc.xml -->
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after the lifecycleBeanProcessor has run -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true">
</property></bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager">

</property></bean>

3.spring-shiro.xml (definition)

<!--?xml version="1.0" encoding="UTF-8"?-->
<beans xmlns="https://www.springframework.org/schema/beans" xmlns:util="https://www.springframework.org/schema/util" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="https://www.springframework.org/schema/beans
      https://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
      https://www.springframework.org/schema/util
      https://www.springframework.org/schema/util/spring-util-3.0.xsd">
<description>Shiro 配置</description>

<bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
<!--Set custom realm -->
<property name="realm" ref="monitorRealm">
</property></bean>
<!--Custom Realm inherited from AuthorizingRealm, that is, specifying Shiro to verify the user's authentication and authorization-->
<!--Custom Realm inherits from AuthorizingRealm -->
<bean class="com.shiro.service.MonitorRealm" id="monitorRealm"></bean>

<!-- The Shiro main filter itself is very powerful, its power is that it supports the execution of any custom filter based on URL path expressions-->
<bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter">
<!-- Shiro's core security interface, this attribute is required -->
<property name="securityManager" ref="securityManager">
<!-- The link when logging in is required, a non-essential attribute, by default it will automatically find the "/login.jsp" page in the root directory of the Web project -->
<property name="loginUrl" value="/login.jsp">
<!-- The connection displayed when the user accesses a resource that is not authorized for him -->
<property name="unauthorizedUrl" value="/error/noperms.jsp">
<property name="filterChainDefinitions">
<value>
<!-- Definition of Shiro filter chain -->
<!--
      Anon: no filter specified
      Authc: Verification, these pages can only be accessed after verification, that is, we can only access after logging in.
      -->
<!--The path represented by the first '/' of the value below is relative to the value of HttpServletRequest.getContextPath() -->
<!--anon: The corresponding filter is empty and nothing is done. Here, * after .do and .jsp indicates parameters, such as login.jsp?main -->
<!--authc: The page under this filter must be authenticated before it can be accessed. It is an interceptor built in Shiro org.apache.shiro.web.filter.authc.FormAuthenticationFilter-->

      /login.jsp* = anon
      /login.do* = anon
      /index.jsp*= anon
      /error/noperms.jsp*= anon
                /*.jsp* = authc
                /*.do* = authc
            </value>
        </property>
    </property></property></property></bean>
     
        <!-- Guaranteed to implement the bean execution of Shiro's internal lifecycle function -->
    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" id="lifecycleBeanPostProcessor">
         
</bean></beans>

4. Customize RealmMonitorRealm

@Service("monitorRealm")
public class MonitorRealm extends AuthorizingRealm {

   //Get the authentication related information
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(
         AuthenticationToken authcToken) throws AuthenticationException {
        /* Write the login authentication code here */
//      UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
//      User user = userService.get(token.getUsername());
      User user = new User();
      user.setUserName("admin");
      user.setPassword(EncryptUtils.encryptMD5("admin"));


      return new SimpleAuthenticationInfo(user.getUserName(),
            user.getPassword(), getName());
         
        /* //Token - Token based on username and password  
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;  
        //Username and password can be taken out from the token  
        String accountName = token.getUsername();  
           
      // There is no need to compare here, the logic of the comparison will be done by Shiro, we only need to return a correct authentication information related to the token, so only admin/admin123 can pass the authentication on the subsequent login page  
        return new SimpleAuthenticationInfo("admin","admin123",getName());  */
   }

5.LoginController

@Controller
@RequestMapping(value = "login")
public class LoginController {
   /**
    * User login
    *
    * @param user
    * Login user
    * @return
    */
   @RequestMapping(params = "main")
   public ModelAndView login(User user,HttpSession session, HttpServletRequest request) {

      ModelAndView modelView = new ModelAndView();
        /* represents the current user. */
      Subject currentUser = SecurityUtils.getSubject();
      // Get a token based on username and password
      //The token here is called a token, which is equivalent to a form. If you want to verify it, you have to fill in a form, write the user name and password in it, and give it to the comrades of the public security bureau for verification.
      UsernamePasswordToken token = new UsernamePasswordToken(
            user.getUserName(), EncryptUtils.encryptMD5(user.getPassword()));
        /*UsernamePasswordToken token = new UsernamePasswordToken(
                user.getUserName(), user.getPassword());*/
// However, there is a difference between "remembered" and "authenticated":
// Remembered users are only non-anonymous users, you can get user information via subject.getPrincipals(). However, it is not a fully authenticated user. When you access functions that require an authenticated user, you still need to resubmit the authentication information.
// This difference can refer to the Amazon website. The website will remember the logged-in user by default. When you visit the website again, for non-sensitive page functions, the remembered user information will be displayed on the page, but the account information will still be displayed when you visit the website. Login authentication is required again.
      token.setRememberMe(true);

      try {
         //This sentence is to submit an application, whether the verification can pass, that is, to the comrades of the Public Security Bureau. This will call back a method in reaml
         // Call back doGetAuthenticationInfo for authentication
         currentUser.login(token);
      } catch (AuthenticationException e) {
         modelView.addObject("message", "login errors");
         modelView.setViewName("/login");
         e.printStackTrace ();
         return modelView;
      }
      // verify if passed
      if(currentUser.isAuthenticated()){
         user.setUserName("Zhang San");
         session.setAttribute("userinfo", user);
         modelView.setViewName("/main");
      }else{
         modelView.addObject("message", "login errors");
         modelView.setViewName("/login");
      }
      return modelView;
   }

6. The method call of currentUser.login(token); is called to the Subjectsubject = securityManager.login(this, token); method, then jumps to the custom Realm

public void login(AuthenticationToken token) throws AuthenticationException {
   clearRunAsIdentitiesInternal();
   Subject subject = securityManager.login(this, token);

   PrincipalCollection principals;

   String host = null;

   if (subject instanceof DelegatingSubject) {
      DelegatingSubject delegating = (DelegatingSubject) subject;
      //we have to do this in case there are assumed identities - we don't want to lose the 'real' principals:
      principals = delegating.principals;
      host = delegating.host;
   } else {
      principals = subject.getPrincipals();
   }

   if (principals == null || principals.isEmpty()) {
      String msg = "Principals returned from securityManager.login( token ) returned a null or " +
            "empty value.  This value must be non null and populated with one or more elements.";
      throw new IllegalStateException(msg);
   }
   this.principals = principals;
   this.authenticated = true;
   if (token instanceof HostAuthenticationToken) {
      host = ((HostAuthenticationToken) token).getHost();
   }
   if (host != null) {
      this.host = host;
   }
   Session session = subject.getSession(false);
   if (session != null) {
      this.session = decorate(session);
   } else {
      this.session = null;
   }
}

 

Summarize:

The above is a simple login authentication process of Shiro. In fact, this part of the function is to help us verify whether the user can log in to the system . It is the same function as our ordinary login. Shiro helps us encapsulate this part of the content. Let us not need to write the login verification into the program, but use the configuration method to respond more flexibly to changes, which is in line with the OCP principle we said.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325862166&siteId=291194637