Shiro permission management framework (1)

Shiro Authority Management Framework

1. Introduction

Insert picture description here
Function introduction:
Insert picture description hereInsert picture description here
Shiro architecture
Insert picture description hereInsert picture description here
Insert picture description hereInsert picture description here

2. Run Shiro project

Create the Maven project
pom file as follows:

	<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
	</dependency>
	<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
 	</dependency>

Create a log configuration file

# For all other servers: Comment out the Log4J listener in web.xml to activate Log4J.
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
#begin
#for normal test,delete when online
log4j.logger.com.ibatis=DEBUG
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
log4j.logger.java.sql.Connection=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG

Write shiro configuration file

[users]
root=123456,admin
test=123456,role1,role2

[roles]
admin=*
role1=index.html,user.html
role2=user.html,menu.html
;anon 不进行任何验证
;authc 必须登录后才能访问
[urls]
/login.html=anon
/index.html=authc
/role.html=authc,roles[admin]
/menu/**=authc,roles[admin],perms[menu:*]

Write test class (account information exists in the configuration file)

public class ShiroTest {
    
    

    public static void main(String[] args) {
    
    

        Factory<SecurityManager> factory= new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager=factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject currentUser=SecurityUtils.getSubject();
        System.out.println("是否登录"+currentUser.isAuthenticated());
        UsernamePasswordToken token=new UsernamePasswordToken("root","123456");
        try {
    
    
            currentUser.login(token);
        } catch (IncorrectCredentialsException e) {
    
    
            System.out.println("密码不正确");
        }catch (UnknownAccountException e) {
    
    
            System.out.println("用户名不存在");
        }
        System.out.println("登录");
        System.out.println("用户是否拥有admin角色"+currentUser.hasRole("admin"));
        System.out.println("用户能否访问url"+currentUser.isPermitted("index.html"));
        System.out.println("是否登录"+currentUser.isAuthenticated());

    }

}

Write the test class (account information is stored in the database, need to check the database for verification)
Pom file add:

		 <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.11.RELEASE</version>
        </dependency>

The new configuration file is as follows

[main]
dataSource=org.springframework.jdbc.datasource.DriverManagerDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://127.0.0.1:3306/test
dataSource.username=root
#如果数据库没有密码,就不要写这行
dataSource.password=123456
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm

#是否检查权限
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource=$dataSource

#重写sql语句
#根据用户名查询出密码
jdbcRealm.authenticationQuery = select PASSWORD from SHIRO_USER where USER_NAME = ?
#根据用户名查询出角色
jdbcRealm.userRolesQuery = select ROLE_NAME from SHIRO_USER_ROLE where USER_NAME = ?
#根据角色名查询出权限
jdbcRealm.permissionsQuery = select PERM_NAME from SHIRO_ROLE_PERMISSION WHERE ROLE_NAME = ?
securityManager.realms=$jdbcRealm

Why rewrite sql to
view Realm, which is responsible for interacting with the database, we found that the sql statement has been defined,And here the structure of sql cannot be changed. For example, select password from users where username = ?, no matter how the field name or table name changes, jdbcrealm is used to check the password according to the user name from the user table. Even if you want to check the password according to the ID, it is still used as the user name to check .
The significance of rewriting sql is that you can define this sql according to your own table name and field name, although you cannot change the logic of sql.
My database table creation is as follows: the
Insert picture description here
Insert picture description here
Insert picture description here
data in the table is as follows: the
Insert picture description here
Insert picture description here
Insert picture description here
test class is as follows:

public class ShiroTestFromJdbc {
    
    

    public static void main(String[] args) {
    
    

        Factory<SecurityManager> factory= new IniSecurityManagerFactory("classpath:shiro-mysql.ini");
        SecurityManager securityManager=factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject currentUser=SecurityUtils.getSubject();
        System.out.println("是否登录"+currentUser.isAuthenticated());
        UsernamePasswordToken token=new UsernamePasswordToken("admin","123456");
        try {
    
    
            currentUser.login(token);
        } catch (IncorrectCredentialsException e) {
    
    
            System.out.println("密码不正确");
        }catch (UnknownAccountException e) {
    
    
            System.out.println("用户名不存在");
        }
        System.out.println("登录");
        System.out.println("用户是否拥有admin角色"+currentUser.hasRole("admin"));
        System.out.println("用户能否访问url"+currentUser.isPermitted("index.html"));
        System.out.println("是否登录"+currentUser.isAuthenticated());
    }
}

Next, we will integrate shiro with the web project and write realm ourselves (the above code uses jdbcrealm)
Add to the pom file:

		<!--shiro web类库-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>

The content of the web.xml file is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <context-param>
        <param-name>shiroEnvironmentClass</param-name>
        <param-value>org.apache.shiro.web.env.IniWebEnvironment</param-value>
    </context-param>
    <!-- 此处用于加载shiro的配置文件-->
    <context-param>
        <param-name>shiroConfigLocations</param-name>
        <param-value>classpath:shiro-mysql.ini</param-value>
    </context-param>
    <listener>
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener>
    <filter>
        <filter-name>ShiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>ShiroFilter</filter-name>
        <url-pattern>*.html</url-pattern>
    </filter-mapping>
</web-app>

Inject a custom realm into the configuration file

myRealmAR=com.hd.shiro.MyRealmAuthorizing
securityManager.realms=$myRealmAR
public class MyRealmAuthorizing extends AuthorizingRealm {
    
    

    @Override
    public String getName() {
    
    
        return "MyRealmAR";
    }

    /**
     * 用于授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    
        System.out.println(principals.getPrimaryPrincipal());
        //根据主键去查询用户权限
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRole("admin");
        authorizationInfo.addRole("test");
        authorizationInfo.addStringPermission("index.html");
        return authorizationInfo;
    }

    /**
     *
     * @param token
     * @return 用于登录
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
        UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
        System.out.println(usertoken.getUsername());
        //根据用户名查询数据库,找到用户真正的密码
        if("admin".equals(usertoken.getUsername())){
    
    
            return new SimpleAuthenticationInfo("admin","123456",getName());
        }else{
    
    
            throw new UnknownAccountException(); //如果用户名错误
        }
    }
}

Specific source code address: https://gitee.com/hdyxk/shiro-framework-learning .
Note:
1.

 /=authc
            /login.html=anon
            /index.html = authc
            /role.html=authc,roles[admin]
            /menu/**=authc,roles[admin,test],perms[menu:*]

/menu/** At the same time, both roles and permissions are required to be able to access, but in practice, we hope that as long as the roles and permissions are satisfied, we only need to rewrite our own permissions filter.

public class MyPermFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception {
        String[] perms = (String[]) mappedValue;
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        for (String p : perms) {
            if (subject.isPermitted(p)) {
                session.setAttribute("Allowed", true);
                return true;
            }
        }
        return false;
    }
}
public class MyRoleFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object mappedValue) throws Exception {
        String[] roles = (String[]) mappedValue;

        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        if (session.getAttribute("Allowed") != null) {
            return true;
        }
        for (String role : roles) {
            if (subject.hasRole(role)) {
                return true;
            }
        }
        return false;
    }
}

Add in shirofilter bean:

</property>
        <property name="filters">
            <map>
                <entry key="roles">
                    <bean class="filter.MyRoleFilter"/>
                </entry>
                <entry key="perms">
                    <bean class="filter.MyPermFilter"/>
                </entry>
            </map>
        </property>

two.

 @RequiresPermissions("menu:edit")
    @RequestMapping("/menu/list.html")
    public String list() {
        return "menu";
    }

Using annotations allows fine-grained permission control. For example, you must have menu.edit permission to access list.html.
3. If the permissions fail seriously, we can set enhancements, encapsulate the exceptions, and return the specified information

@ControllerAdvice
public class AuthExceptionHandler {
    
    
    @ExceptionHandler({
    
    UnauthorizedException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
    
    
        ModelAndView mv = new ModelAndView();
        mv.addObject("exception", e.getMessage());
        mv.setViewName("error");
        return mv;
    }
}

4. In the shiro filter, the matching rules exist in the database in practice. For this we need to rewrite ShiroFilterFactoryBean

<value>
            /=authc
            /login.html=anon
            /index.html = authc
            /role.html=authc,roles[admin]
            /menu/**=authc,roles[admin,test],perms[menu:*]
        </value>

But just rewrite the method of setting matching rules

public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean {
    
    
    @Override
    public void setFilterChainDefinitions(String definitions) {
    
    
        Ini ini = new Ini();
        ini.load(definitions);
        Ini.Section section = ini.getSection("url s");
        if (CollectionUtils.isEmpty(section)) {
    
    
            section = ini.getSection("");
        }
        section.put("/menu/**","roles[admin]");
        this.setFilterChainDefinitionMap(section);
    }
}
 /menu/**=authc,roles[admin,test],perms[menu:*]等价于section.put("/menu/**","roles[admin]");

Guess you like

Origin blog.csdn.net/weixin_44726976/article/details/108747683