PageHelper paging plug-in, spring security, permission control, and AOP logging

Keywords: Java Spring

1, PageHelper paging plug-in

PageHelper is an excellent open-source mybatis paging plug-in in China. It supports basic mainstream and commonly used databases, such as mysql, oracle, mariaDB, DB2, SQLite, Hsqldb, etc.

There are two ways to reference the PageHelper paging plug-in:

  • Introduce Jar package;
  • Use Maven (recommended);

1.1 reference Jar package to realize paging

Download the latest version of the jar package from the address below

Because the sql parsing tool is used, you also need to download jsqlparser.jar:

1.2 paging using Maven

1.2.1 PageHelper coordinates

<!--Paging plug-in-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.8</version>
</dependency>

1.2.2 configure interceptor plug-in

  • Configure the interceptor plug-in in MyBatis configuration XML (or write in sqlSessionFactory object configuration of spring.xml)
<!-- 
    plugins The position in the configuration file must meet the requirements, otherwise an error will be reported, in the following order:
    properties?, settings?, 
    typeAliases?, typeHandlers?, 
    objectFactory?,objectWrapperFactory?, 
    plugins?, 
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
    <!-- com.github.pagehelper by PageHelper Package name of class -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- Use the following method to configure parameters. All parameters will be described later -->
        <property name="param1" value="value1"/>
	</plugin>
</plugins>
<!--3.to configure SqlSessionFactory object-->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--specify data source-->
        <property name="dataSource" ref="dataSource"/>
        <!-- to configure MyBaties Global profile:mybatis-config.xml -->
        <!--        <property name="configLocation" value="classpath:mybatis-config.xml" />-->
        <!--Specifies the alias of the entity class-->
        <property name="typeAliasesPackage" value="cn.tsm.bean"/>
        <!--to configure mapper.xml file location-->
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
        <!--to configure PageHelper Interceptor plug-in-->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor"></bean>
            </array>
        </property>
    </bean>
  • Configure the interceptor plug-in in the Spring configuration file
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- Note other configurations -->
    <!-- afferent PageHelper Plug in for -->
    <property name="plugins">
        <array>
            <!-- Objects passed in to the plug-in -->
            <bean class="com.github.pagehelper.PageInterceptor">
                <property name="properties">
                    <props>
                        <prop key="helperDialect">mysql</prop>
                        <prop key="reasonable">true</prop>
                    </props>
                </property>
            </bean>
        </array>
    </property>
</bean>

1.2.3 introduction to paging plug-in parameters

1. helperDialect: The paging plug-in will automatically detect the current database link and automatically select the appropriate paging method. You can configure helperDialect Property to specify which dialect the paging plug-in uses. When configuring, you can use the following abbreviations:
    oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby
    Special note: use SqlServer2012 The database needs to be manually specified as sqlserver2012,Otherwise, it will be used SqlServer2005 Pagination in the same way. You can do it, too AbstractHelperDialect,Then configure the property to the fully qualified name of the implementation class to use the custom implementation method.

2. offsetAsPageNum: The default value is false,This parameter is used for RowBounds Valid as paging parameter. When this parameter is set to true When, the RowBounds Medium offset Parameter as pageNum You can use page number and page size to page.

3. rowBoundsWithCount: The default value is false,This parameter is used for RowBounds Valid as paging parameter. When this parameter is set to true When using RowBounds Paging occurs count Query.

4. pageSizeZero: The default value is false,When this parameter is set to true When, if pageSize=0 perhaps RowBounds.limit = 0 All the results will be queried (equivalent to that the paging query is not executed, but the returned result is still Page Type).

5. reasonable: Paging rationalization parameter. The default value is false. When this parameter is set to true When, pageNum<=0 The first page will be queried, pageNum>pages(When the total number is exceeded, the last page will be queried. default false Query directly according to parameters.

6. params: To support startPage(Object params)Method is added to configure the parameter mapping, which is used to take values from the object according to the attribute name, which can be configured pageNum,pageSize,count,pageSizeZero,reasonable,The default value for mapping is not configured. The default value is pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero. 

7. supportMethodsArguments: Support through Mapper Interface parameters to pass paging parameters. The default value is false,The paging plug-in will automatically select the parameter values of the query method according to the above parameters params Take values in the configured fields, and the page will be automatically paged when an appropriate value is found. For the usage method, refer to the in the test code com.github.pagehelper.test.basic Under the bag ArgumentsMapTest and ArgumentsObjTest. 

8. autoRuntimeDialect: The default value is false. Set to true At runtime, it is allowed to automatically identify the paging of the corresponding dialect according to multiple data sources (automatic selection is not supported) sqlserver2012,Only use sqlserver),Refer to scenario 5 below for usage and precautions.

9. closeConn: The default value is true. When using run-time dynamic data sources or not set helperDialect Property to automatically obtain a database connection. You can use this property to set whether to close the obtained connection. By default true Off, set to false After, the obtained connection will not be closed. The setting of this parameter depends on the data source you choose.

10. aggregateFunctions(5.1.5+): The default is the aggregate function of all common databases. It allows you to add aggregate functions manually (affecting the number of rows). All functions starting with aggregate functions are added in the count When converting, a layer will be set. Other functions and columns will be replaced with count(0),among count Columns can be configured by themselves.

1.3 cases

  • service interface
public interface ProductService {
    public List<Product> findAllProduct(int pageNum,int pageSize) ;
}
  • service implementation class
@Service
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductMapper productMapper;
    
    @Override
    public List<Product> findAllProduct(int pageNum,int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return productMapper.findAllProduct();
    }
}
  • controller
@RequestMapping("/findAllProduct")
public String findAllProduct(@RequestParam(name = "page" ,defaultValue = "1") int pageNum,
                             @RequestParam(name = "size" ,defaultValue = "2") int pageSize,
                             Model model){
    List<Product> productList = productService.findAllProduct(pageNum,pageSize);
    PageInfo pageInfo = new PageInfo(productList);

    model.addAttribute("pageInfo", pageInfo);
    return "product-list";
}
  • Front page
<c:forEach items="${pageInfo.list}" var="product">
    <tr>
        <td><input name="ids" type="checkbox"></td>
        <td>${product.id }</td>
        <td>${product.productNum }</td>
        <td>${product.productName }</td>
        <td>${product.cityName }</td>
        <td>${product.departureTimeStr }</td>
        <td class="text-center">${product.productPrice }</td>
        <td>${product.productDesc }</td>
        <td class="text-center">${product.productStatusStr }</td>
        <td class="text-center">
            <button type="button" class="btn bg-olive btn-xs">order</button>
            <button type="button" class="btn bg-olive btn-xs">details</button>
            <button type="button" class="btn bg-olive btn-xs">edit</button>
        </td>
    </tr>
</c:forEach>
  • Add page number operation
<div class="box-footer">
    <div class="pull-left">
        <div class="form-group form-inline">
            in total ${pageInfo.pages} Page of ${pageInfo.total} Data per page
            <select class="form-control">
                <option>1</option>
                <option>2</option>
                <option>3</option>
                <option>4</option>
                <option>5</option>
            </select> strip
        </div>
    </div>

    <div class="box-tools pull-right">
        <ul class="pagination">
            <li><a href="${pageContext.request.contextPath}/product/findAllProduct?page=1&size=${pageInfo.pageSize}" aria-label="Previous">home page</a></li>
            <li><a href="${pageContext.request.contextPath}/product/findAllProduct?page=${pageInfo.pageNum-1}&size=${pageInfo.pageSize}">previous page</a></li>
            <c:forEach begin="1" end="${pageInfo.pages}" var="p">
                <li><a href="${pageContext.request.contextPath}/product/findAllProduct?page=${p}&size=${pageInfo.pageSize}">${p}</a></li>
            </c:forEach>
            <li>
                <a href="${pageContext.request.contextPath}/product/findAllProduct?page=${pageInfo.pageNum+1}&size=${pageInfo.pageSize}">next page</a>
            </li>
            <li><a href="${pageContext.request.contextPath}/product/findAllProduct?page=${pageInfo.pages}&size=${pageInfo.pageSize}" aria-label="Next">Last page</a></li>
        </ul>
    </div>
</div>
  • Customize the number of entries displayed per page

in total ${pageInfo.pages} Page of ${pageInfo.total} Data per page
<select class="form-control" id="changePageSize" onclick="changePageSize()">
    <option>1</option>
    <option>2</option>
    <option>3</option>
    <option>4</option>
    <option>5</option>
</select> strip
function changePageSize() {
    //Gets the value of the drop-down box
    var pageSize = $("#changePageSize").val();

    //Send a request to the server to change the number of displayed pages
    location.href = "${pageContext.request.contextPath}/product/findAllProduct?page=1&size="
    + pageSize;
}

2, Spring security

2.1 what is spring security

The predecessor of Spring Security is Acegi Security, which is a framework used by the Spring project team to provide security authentication services.

( https://projects.spring.io/spring-security/ )Spring security provides comprehensive security services for enterprise applications based on J2EE

It is an enterprise software project developed using the Spring framework, a leading J2EE solution. People use Spring Security for many reasons, but it is usually attractive

They are cited that they can't find solutions for typical enterprise application scenarios in J2EE Servlet specification or EJB specification. In particular, they can't find solutions anymore

Migrate at the WAR or EAR level. In this way, if you change the server environment, you need to do a lot of work in the new target environment to improve your application

Reconfigure the security of the system. Using Spring Security solves these problems and provides you with many useful and other security that can be specified

Full feature. Safety includes two main operations.

  • "Authentication" is to establish a declared subject for the user. The general formula of subject refers to the user, device or other system that can perform actions in your system

System.

  • "Authorization" refers to whether a user can perform an operation in your application. Before reaching the authorization judgment, the subject of identity has been verified by authentication

The process is established.

These concepts are universal and not unique to Spring Security. At the authentication level, Spring Security widely supports various authentication modes,

Most of these validation models are provided by third parties or relevant standards institutions under development, such as Internet Engineering Task

Force. As a supplement, Spring Security also provides its own set of authentication functions.

Spring Security currently supports the following authentication technology: HTTP BASIC authentication headers (an IEFT RFC based authentication header)

HTTP Digest authentication headers (a standard based on IEFT RFC) http X.509 client certi? Cat exchange

(a standard based on IEFT RFC) LDAP (a very common cross platform authentication practice, especially in large environments) form based

Authentication OpenID authentication Computer Associates Siteminder JA-SIG

Central Authentication Service (CAS, a popular open source single sign on system) Transparent authentication context

propagation for Remote Method Invocation and HttpInvoker (a Spring remote invocation protocol)

2.2 spring security quick start

2.2.1 spring security coordinates

<!--introduce Security Certification services framework coordinates-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>

2.2.2 configuring web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-security.xml</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

2.2.3 configuring spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/security
      http://www.springframework.org/schema/security/spring-security.xsd
    ">

    <!-- Configure non intercepted resources security Is certification required none Representative does not need-->
    <security:http pattern="/pages/login.jsp" security="none"/>
    <security:http pattern="/pages/fail.jsp" security="none"/>
    <security:http pattern="/css/**" security="none"/>
    <security:http pattern="/img/**" security="none"/>
    <security:http pattern="/plugins/**" security="none"/>

    <!--
    use-expressions: access Use expression
    -->
    <security:http auto-config="true" use-expressions="false" >
        <security:intercept-url pattern="/**" access="ROLE_USER,ROLE_ADMIN" />
        <!-- Define the specific page to jump to
            login-page Specify the login page path address
            login-processing-url: Specify the login request to be intercepted (the user name and password entered by the user can be obtained through the request, which will be executed automatically userService in loadUserByName Method)
         -->
        <security:form-login
            login-page="/pages/login.jsp"
            login-processing-url="/login"
            default-target-url="/pages/main.jsp"
            authentication-failure-url="/pages/fail.jsp"
            authentication-success-forward-url="/pages/main.jsp"
         />

        <!-- Turn off cross domain requests -->
        <security:csrf disabled="true"/>
    </security:http>

    <!-- Switch to the user name and password in the database -->
    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userService">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!--Specifies how the password is encrypted-->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

    <bean id="webSecurityExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"> </bean>
</beans>

2.2.4 writing UserMapper

public interface UserMapper {
    // Query users by user name
    public UserInfo findUserByUserName(String username);
}

2.2.5 writing UserService

package cn.yunhe.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface UserService extends UserDetailsService {
}
@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //Query user information according to user name
        User user = userDao.queryUserByName(username);
        org.springframework.security.core.userdetails.User securityUser =
                //{noop} means that password encryption is not required
                new org.springframework.security.core.userdetails.User(
                        user.getUsername(),
//                        "{noop}"+user.getPassword(),
                        user.getPassword(),
                        user.getStatus() == 1 ? true : false,
                        true, true, true,
                        getAuthority(user.getRoleList()));
        return securityUser;
    }

    /**
     * List of simulated user roles (to be replaced later by querying the users role table)
     *
     * @return
     */
    public List<SimpleGrantedAuthority> getAuthority(List<Role> roleList) {
        ArrayList<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<SimpleGrantedAuthority>();
        for (Role role : roleList) {
            String roleName = role.getRoleName();
            simpleGrantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + roleName));
        }
        return simpleGrantedAuthorities;
    }
}

2.2.6 writing UserMapper.xml

    <!--users,role Two table mapping-->
    <resultMap id="queryUserByNameMap" type="user">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <result column="email" property="email"/>
        <result column="password" property="password"/>
        <result column="phoneNum" property="phoneNum"/>
        <result column="status" property="status"/>
        <!--Users and roles one to many-->
        <collection property="roleList" ofType="role">
            <id column="rid" property="id"/>
            <result column="roleName" property="roleName"/>
            <result column="roleDesc" property="roleDesc"/>
        </collection>
    </resultMap>

    <!--Query user information through user name (user information, role information)-->
    <select id="queryUserByName" parameterType="String" resultMap="queryUserByNameMap">
        select users.id uid,
               users.username,
               users.password,
               users.email,
               users.phoneNum,
               users.status,
               r.id     rid,
               r.roleName,
               r.roleDesc
        from users
                 join users_role ur on users.id = ur.userId
                 join role r on r.id = ur.roleId
        where username = #{username}
    </select>

3, AOP log

  • AOP coordinates
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>
  • Enable AOP auto proxy annotation
<!--
        support AOP Annotation support for, AOP The bottom layer uses agent technology
        JDK Dynamic proxy requires an interface
        cglib Proxy, generate subclass objects, proxy-target-class="true" Default use cglib The way
    -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
  • Configure the listener of request in web.xml
<listener>
   <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
  • Create AOP facet class under controller
@Component
@Aspect
public class LogAOP {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private SysLogService sysLogService;

    private Date visitTime;
    private Class clazz;
    private Method method;


    @Before("execution(* cn.yunhe.controller.*.*(..))")
   // * cn.yunhe.controller.*.*(..)
    public void doBefore(JoinPoint jp) throws NoSuchMethodException {

        visitTime = new Date();//The current time is the time when the access started
        clazz = jp.getTarget().getClass(); //Specific class to access
        String methodName = jp.getSignature().getName(); //Gets the name of the method accessed
        Object[] args = jp.getArgs();//Gets the parameters of the accessed method


        //Gets the Method object of the Method to be executed
        if (args == null || args.length == 0) {
            method = clazz.getMethod(methodName); //Only methods without parameters can be obtained
        } else {
            Class[] classArgs = new Class[args.length];
            for (int i = 0; i < args.length; i++) {

                if(args[i].getClass() == BindingAwareModelMap.class){
                    classArgs[i] = Model.class;
                }else{
                    classArgs[i] = args[i].getClass();
                }
            }
            method = clazz.getMethod(methodName, classArgs);
        }
    }



    @After("execution(* cn.yunhe.controller.*.*(..))")
    public void doAfter(JoinPoint jp){
        long time = new Date().getTime() - visitTime.getTime(); //Gets the duration of the access

        String url = "";
        //Get url
        if (clazz != null && method != null && clazz != LogAOP.class) {
            //1. Get @ RequestMapping("/xxx") on the class
            RequestMapping classAnnotation = clazz.getAnnotation(RequestMapping.class);
            if (classAnnotation != null) {
                String[] classValue = classAnnotation.value();
                //2. Get @ RequestMapping(xxx) on the method
                RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                if (methodAnnotation != null) {
                    String[] methodValue = methodAnnotation.value();
                    // Splice url
                    url = classValue[0] + methodValue[0];

                    //Get access ip
                    String ip = request.getRemoteAddr();

                    //Gets the user of the current operation
                    SecurityContext context = SecurityContextHolder.getContext();//The currently logged in user was obtained from the context
                    User user = (User) context.getAuthentication().getPrincipal();
                    String username = user.getUsername();

                    //Encapsulate log related information into SysLog objects
                    SysLog sysLog = new SysLog();
                    sysLog.setExecutionTime(time); //Execution duration
                    sysLog.setIp(ip);
                    sysLog.setMethod("[Class name] " + clazz.getName() + "[Method name] " + method.getName());
                    sysLog.setUrl(url);
                    sysLog.setUsername(username);
                    sysLog.setVisitTime(visitTime);

                    //Call Service to complete the operation
                    sysLogService.addSysLog(sysLog);
                }
            }
        }

    }

}

Posted by rheroux on Tue, 26 Oct 2021 20:49:13 -0700