This is how I use shiro

Keywords: Java Shiro xml Apache Spring

Brief description

I spent some time on shiro's learning. A big push has been made on shiro's information network. The knowledge points I learned before have been recorded on the notes of Dow Cloud. Dow Cloud has its own advantage that it can still record something when there is no network, but the disadvantage is that it can't be shared and discussed with everyone. Finally, I choose segmentFault to discover myself first.The prestige value is negative, a little sad. After learning something in the future, I will write notes here. First, I summarize and comb my knowledge and my memory is not good, so that I can easily recall it later. Second, I hope to discuss with you. I have something wrong that I hope to have a god pointing out.Say nothing more. Write down the usage of Shiro I've recently learned.

text

The general login authentication process is illustrated above. When you enter the login page, you enter the shiroFilter security authentication filter first, and then read the database information to authenticate your login and authorization. This part is handed to realm. When the authentication is successful, you jump to the address corresponding to the success URL. If it fails, you jump to the loginUrl.The ${adminPath} on the diagram simply reads the configuration parameters from the configuration file properties, which can be converted to/a.

web.xml configuration

    <!-- Apache Shiro -->
    <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>/*</url-pattern>
    </filter-mapping>

web.xml is basically a fixed template, /* intercepts all requests, but remember the name shiroFilter.

spring-context-shiro.xml configuration

<description>Shiro Configuration</description>

    <!-- Load Configuration Property File -->
    <context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" />
    
    <!-- (Will be introduced) Shiro Permission Filter Definition -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /static/** = anon
                /userfiles/** = anon
                ${adminPath}/login = authc
                ${adminPath}/logout = logout
                ${adminPath}/sys/** = roles[sys]
                ${adminPath}/cms/** = perms[cms:view]
                ${adminPath}/** = user
            </value>
        </constructor-arg>
    </bean>
    
    <!-- (Extended) Security Authentication Filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" /> 
        <property name="loginUrl" value="${adminPath}/login" />
        <property name="successUrl" value="${adminPath}/success" />
        <property name="unauthorizedUrl" value="/unauthorized.jsp" />
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>
    
    <!-- (Extended) Definition Shiro Security Management Configuration -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- adopt realm To read identity and authorization information -->
        <property name="realm" ref="systemAuthorizingRealm" />
    </bean>
    
    <!-- (Fixed) Guaranteed implementation Shiro inside lifecycle Functional bean implement -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!--(Fixed) AOP Style Method Level Permission Check  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

Many of the XML configuration files are also fixed. Official Web documents also describe that anon indicates that the path corresponding to the request is not authenticated, authc indicates that the path corresponding to the request requires login authentication, roles[sys] indicates that the path corresponding to the request requires a role of sys to be allowed, and perms[cms:view] indicates that the path corresponding to the request requires cms:view permission to allow.

<property name="loginUrl" value="${adminPath}/login" />

Indicates that a failed login will jump to the corresponding request

<property name="successUrl" value="${adminPath}/success" />

Requests indicating that a successful login will jump

<property name="unauthorizedUrl" value="/unauthorized.jsp" />

Indicates a request to jump after accessing a link that is not authorized to access.

<entry key="authc" value-ref="formAuthenticationFilter"/> 

Indicates that authc is validated by the FormAuthenticationFilter class

<property name="realm" ref="systemAuthorizingRealm" />

This is our custom realm to authenticate login and authorization information

The directory structure of the experiment, as shown above

Override FormAuthenticationFilter class

@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter{
    
    public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
    
    private String captchaParam = DEFAULT_CAPTCHA_PARAM;

    @Override
    protected AuthenticationToken createToken(ServletRequest request,ServletResponse response) {
        System.out.println("-------------Enter Creation token Method-------------");
        // TODO Auto-generated method stub
        String username = getUsername(request);
        String password = getPassword(request);
        if(password == null){
            password = "";
        }
        String captcha = getCapcha(request);
        System.out.println("-------------Output Creation token Method-------------");
        return new UsernamePasswordToken(username,password,captcha);
    }
    
    
    public String getCapcha(ServletRequest request){
        return WebUtils.getCleanParam(request, captchaParam);
    }
    
    public String getSuccessUrl(){
        return super.getSuccessUrl();
    }
    
    @Override
    protected void issueSuccessRedirect(ServletRequest request,ServletResponse response) throws Exception {
        // TODO Auto-generated method stub,
        System.out.println("-------------issueSuccessRedirect-------------");
        WebUtils.issueRedirect(request, response, getSuccessUrl());
    }


    @Override
    protected boolean onLoginFailure(AuthenticationToken token,AuthenticationException e, ServletRequest request,
            ServletResponse response) {
        // TODO Auto-generated method stub
        System.out.println("-------------onLoginFailure-------------");
        String className = e.getClass().getName();
        String message = "";
        System.out.println("=========e.getCLass().getName()===========:"+className);
        if(IncorrectCredentialsException.class.getName().equals(className)
                || UnknownAccountException.class.getName().equals(className)){
            message = "ERROR Incorrect username or password";
        }else{
            message = "There is a problem with the system. Please try again later!";
            e.printStackTrace(); // Output to console
        }
        request.setAttribute("message",message);
        return true;
    }

}

Why do we need to rewrite a shiroFilter security authentication filter when we have entered the user's password? The essence is that shiro has provided us with a corresponding FormAuthenticationFilter class in which tokens can be created for later process authentication based on the information entered by the user, but if we want to add something else to itWhen we create this token together, for example, by using username, password, authentication code, whether we remember me, whether we are on the phone, and so on, we can override some of the methods in the FormAuthenticationFilter.

WebUtils.getCleanParam(request, captchaParam) is an encapsulation provided to us by shiro. It is actually an encapsulation of request.getParameter(paramName), through which we can get the parameters entered in the foreground.

Override UsernamePasswordToken class

public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken{
    
    private static final long serialVersionUID = 1L;
    
    private String captcha;
    
    public UsernamePasswordToken(String username,String password){
        super(username,password);
    }

    public UsernamePasswordToken() {
        super();
    }
    
    public UsernamePasswordToken(String username,String password,String captcha){
        super(username,password);
        this.captcha = captcha;
    }

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }
    
}

By overriding UsernamePasswordToken, the goal is to construct a new UsernamePasswordToken for use based on the parameters the developer wants to create a token.I have written only the username password parameter here to simplify.

Now that we have token, let's start our certification.

Authenticate Authorization with Custom Realm

@Service
public class SystemAuthorizingRealm extends AuthorizingRealm{
    
    @Autowired
    private SystemService systemService;
    /**
     * Logon Authentication
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        // TODO Auto-generated method stub
        System.out.println("-------------Enter Logon Authentication Method-------------");
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = token.getUsername();
        User user = systemService.getUserByName(username);
        System.out.println("-------1----getName()--------:"+getName());
        if(user != null){
            System.out.println("-------2----getName()--------:"+getName());
            System.out.println("-------------Logon Authentication End--Return SimpleAuthenticationInfo-------------");
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
        }else{
            System.out.println("------------Logon Authentication End--Return null------------");
            return null;
        }
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // TODO Auto-generated method stub
        System.out.println("-------------Enter Authorization Authentication Method-------------");
//        Principal principal = (Principal) getAvailablePrincipal(principals);
        String name = (String) principals.getPrimaryPrincipal();
//        User user = systemService.getUserByName(principal.getName());
        User user = systemService.getUserByName(name);
        if(user != null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            List<Role> Roles = systemService.getRoleByUserId(user.getId());
            for(Role r:Roles){
                System.out.println("role---> "+r.getRole());
                info.addRole(r.getRole());
                List<Perm> Perms = systemService.getPermByRoleId(r.getId());
                if(Perms != null && Perms.size() >0 ){
                    for(Perm p:Perms){
                        System.out.println("perm---> "+p.getPermission());
                        info.addStringPermission(p.getPermission());
                    }
                }
            }
            System.out.println("-----------End of Authorization Authentication--Return info------------");
            return info;
        }else{
            System.out.println("-----------End of Authorization Authentication--Return null------------");
            return null;
        }
    }
}

In realm we need to inherit AuthorizingRealm and override two methods:

  1. doGetAuthenticationInfo method: User authentication method, returned by parameter
    SimpleAuthenticationInfo jumps to the successUrl address in the xml configuration file if the login succeeds and to the loginUrl address if the login fails.

  2. doGetAuthorizationInfo method: Authentication of identity rights, using SimpleAuthorizationInfo to authorize roles and privileges to SimpleAuthorizationInfo; in shiro, if an address is accessed that is not authorized, it jumps to the xml configuration file
    The address corresponding to unauthorizedUrl.

Basically shiro is done here, when we sign in in:

The diagram above shows the successful login process

The diagram above shows the process of login failure

At this time shiro has remembered the user's information and will not continue validating when requesting a path again. When requesting a blocked connection here, it will go directly to find the corresponding action instead of entering the security filter. All we want to log in here needs to exit the current user.The following code can exit the current user so that we can continue to authenticate with security when we request to log in again.

SecurityUtils.getSubject().logout();

When our login is successful, access to a/sys can be requested successfully by a user who has the sys role but does not have perms[cms:view] privilege, but access to a/cms jumps to the unauthorizedUrl address of shiro's xml profile, indicating that the privilege is inaccessible.

The above illustration shows that authorization succeeded, and the user has the right to request it, jumping to the corresponding action

Authorization failed in the figure above, jumping to unauthorized.jsp page, indicating that you are not authorized to access

Posted by fredriksk on Sun, 30 Jun 2019 11:22:20 -0700