shiro learning ——beetl matching shiro

Go directly to the text:

The first is that the pom file needs to add these packages

<!-- 添加shiro相关包 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.2.3</version>
		</dependency>
		<!-- 添加shiro web支持 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>1.2.3</version>
		</dependency>
		<!-- 添加shiro spring支持 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency> 
		    <groupId>org.apache.shiro</groupId> 
		    <artifactId>shiro-ehcache</artifactId> 
		    <version>1.2.3</version> 
		</dependency>

Realize our own realm

/**
 * @author panmingshuai
 * @description
 * @Time 2018年3月17日 下午5:05:27
 *
 */
public class MyShiroRealm extends AuthorizingRealm {
	
	@Autowired
	private UserService userService;

	@Override
	/**
	 * 获取授权信息,登录成功后在访问地址变化时调用
	 */
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		String userName = (String) principalCollection.fromRealm(getName()).iterator().next();
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		
		if(StringUtils.isNotBlank(userName)){
			info.addRoles(userService.getRolesByName(userName));//添加相应角色
			info.addStringPermissions(userService.getPermissByName(userName));//添加相应权限
			
			return info;
		}
		return null;
	}

	@Override
	/**
	 * 登录验证,会先进/login的controller方法验证登录,然后在subject.login(token)时进入这个方法判断。
	 */
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
		// 通过表单接收的用户名
		String userName = token.getUsername();
		if(StringUtils.isNotBlank(userName)){
			return new SimpleAuthenticationInfo(userName, userService.getUserByName(userName).getPwd(), getName());//这里的getName方法指的是get现在这个realm类的名字
		}
		return null;
	}

}

Next is the configuration file, web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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">
	
	<!-- 启动Spring大容器,将Spring容器内的内容纳入到WEB容器中 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-context.xml</param-value>
	</context-param>	
	<context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:log4j.properties</param-value>
    </context-param>
    <listener>
        <description>日志监听</description>
        <display-name>log4jConfigLocation</display-name>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
	<listener>
		<description>spring监听器</description>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
    
    <!-- 配置前端控制器 -->
	<servlet>
		<servlet-name>springmvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-servlet.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>springmvc</servlet-name>
		<!-- 在DispatcherServlet中/代表所有,其他地方都是/* -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 添加shiro过滤器 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<!-- 该值缺省为false,表示声明周期由SpringApplicationContext管理,设置为true表示ServletContainer管理 -->
			<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-app>

Next is: The configuration of shiro in spring-context.xml is as follows:

<!-- 因为shiro的相关登录信息是存在缓存里的所有必须配置ehcache -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
	</bean>

	<!-- shiro配置 -->
	<!-- 自定义Realm -->
	<bean id="myRealm" class="org.pan.shiro.shiro.MyShiroRealm" />
	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myRealm" />
		<property name="cacheManager" ref="cacheManager" />
	</bean>

	<aop:config proxy-target-class="true"></aop:config>
	<!-- Shiro过滤器 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- Shiro的核心安全接口,这个属性是必须的 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 如果在下面的权限链中没有相应的权限,则跳转到指定页面这里指定的页面 -->
		<property name="unauthorizedUrl" value="/user/unauthorized" />
		<!-- Shiro连接约束配置,即过滤链的定义 -->
		<property name="filterChainDefinitions">
			<value>
				<!-- 表示都可以访问 -->
				/user/login*/**=anon
				<!-- 只有拥有admin角色的用户才可访问,同时需要拥有多个角色的话,用引号引起来,中间用逗号隔开,单个不需引号,但是这是且条件,即要满足这两个角色才能访问这个地址 -->
				/user/student*/**=roles[teacher]
				<!-- perms表示需要该权限才能访问的页面 -->
				/user/teacher*/**=perms[admin]
				<!-- authc表示需要认证才能访问的页面 -->
				/**=authc
			</value>
		</property>
	</bean>

The roles above represent the path that requires the corresponding role to access, and then log in to the controller

@Controller
@RequestMapping("user")
public class ShiroController {
	
	@RequestMapping("/login")
	public ModelAndView login(String userName, String pwd){
		ModelAndView mav = new ModelAndView();
//		这里需要加上判断账号,密码是否正确的方法
		if(StringUtils.isBlank(userName)){
			return new ModelAndView("/html/login.html");
		}
		
		SecurityUtils.getSecurityManager().logout(SecurityUtils.getSubject());//如果原来有的话,就退出
        //登录后存放进shiro token
        UsernamePasswordToken token=new UsernamePasswordToken(userName, pwd);
        Subject subject=SecurityUtils.getSubject();
        subject.login(token);
        
        //如果密码账号正确会执行下面的代码,否则会报错
        mav.setViewName("/html/success.html");
        return mav;
	}
	
	@RequestMapping(value = "/teacher")
	public ModelAndView teacher(){
		return new ModelAndView("/html/teacher.html");
	}
	
	@RequestMapping(value = "/student")
	public ModelAndView student(){
		return new ModelAndView("/html/student.html");
	}
}

Now you can run the project, and you can see that users without amdin permissions access the /user/teacher/* path and will be transferred to the /user/unauthorized path.

Next comes the question on the page,

Although the above method can prevent the user's access from the access point, the corresponding access label still exists on the page. How to make this label invisible after logging in to the system by people who do not have the corresponding permissions? This involves tags. Although jsp also has corresponding tags, I have been using layui, so I have always written layui, and then used beetl as a static template, so here is the practice of beetl, but jsp is exactly the same as this, Because beetl is made with reference to jsp practices. To use the permission tag of beetl, you need to add an extension class

public class ShiroExt {
    /**
     * 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。
     *
     * @return
     */
    public boolean isGuest() {
        return getSubject() == null || getSubject().getPrincipal() == null;
    }

    /**
     * 认证通过或已记住的用户。
     *
     * @return
     */
    public boolean isUser() {
        return getSubject() != null && getSubject().getPrincipal() != null;
    }

    /**
     * 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。
     *
     * @return
     */
    public boolean isAuthenticated() {
        return getSubject() != null && getSubject().isAuthenticated();
    }
    
    /**
     * 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。
     * @return
     */
    public boolean isNotAuthenticated() {
        return !isAuthenticated();
    }

    /**
     * 输出当前用户信息,通常为登录帐号信息
     *
     * @param map
     * @return
     */
    public String principal(Map map) {
        String strValue = null;
        if (getSubject() != null) {

            // Get the principal to print out
            Object principal;
            String type = map != null ? (String) map.get("type") : null;
            if (type == null) {
                principal = getSubject().getPrincipal();
            } else {
                principal = getPrincipalFromClassName(type);
            }
            String property = map != null ? (String) map.get("property") : null;
            // Get the string value of the principal
            if (principal != null) {
                if (property == null) {
                    strValue = principal.toString();
                } else {
                    strValue = getPrincipalProperty(principal, property);
                }
            }

        }

        if (strValue != null) {
            return strValue;
        } else {
            return null;
        }
    }

    /**
     * 验证当前用户是否属于该角色。
     *
     * @param roleName
     * @return
     */
    public boolean hasRole(String roleName) {
        return getSubject() != null && getSubject().hasRole(roleName);
    }

    /**
     * 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
     *
     * @param roleName
     * @return
     */
    public boolean lacksRole(String roleName) {
        boolean hasRole = getSubject() != null
                && getSubject().hasRole(roleName);
        return !hasRole;
    }

    /**
     * 验证当前用户是否属于以下任意一个角色。 以逗号分隔
     *
     * @param roleNames
     * @return
     */
    public boolean hasAnyRole(String roleNames) {
        boolean hasAnyRole = false;

        Subject subject = getSubject();

        if (subject != null) {

            // Iterate through roles and check to see if the user has one of the
            // roles
            for (String role : roleNames.split(",")) {

                if (subject.hasRole(role.trim())) {
                    hasAnyRole = true;
                    break;
                }

            }

        }

        return hasAnyRole;
    }

    /**
     * 验证当前用户是否拥有指定权限。
     *
     * @param p
     * @return
     */
    public boolean hasPermission(String p) {
        return getSubject() != null && getSubject().isPermitted(p);
    }

    /**
     * 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。
     *
     * @param p
     * @return
     */
    public boolean lacksPermission(String p) {
        return !hasPermission(p);
    }

    @SuppressWarnings({ "unchecked" })
    private Object getPrincipalFromClassName(String type) {
        Object principal = null;

        try {
            Class cls = Class.forName(type);
            principal = getSubject().getPrincipals().oneByType(cls);
        } catch (ClassNotFoundException e) {

        }
        return principal;
    }

    private String getPrincipalProperty(Object principal, String property) {
        String strValue = null;

        try {
            BeanInfo bi = Introspector.getBeanInfo(principal.getClass());

            // Loop through the properties to get the string value of the
            // specified property
            boolean foundProperty = false;
            for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
                if (pd.getName().equals(property)) {
                    Object value = pd.getReadMethod().invoke(principal,
                            (Object[]) null);
                    strValue = String.valueOf(value);
                    foundProperty = true;
                    break;
                }
            }

            if (!foundProperty) {
                final String message = "Property [" + property
                        + "] not found in principal of type ["
                        + principal.getClass().getName() + "]";

                throw new RuntimeException(message);
            }

        } catch (Exception e) {
            final String message = "Error reading property [" + property
                    + "] from principal of type ["
                    + principal.getClass().getName() + "]";

            throw new RuntimeException(message, e);
        }

        return strValue;
    }

    protected Subject getSubject() {
        return SecurityUtils.getSubject();
    }
}

This class is written by the author of beetl. If you have any problems, please contact him. But the comments are written by myself, if I make a mistake, I'll ask him.

In order to use it on the page, you need to write a beetlconfig class

/**
 *@description 
 *@auth panmingshuai
 *@time 2018年3月18日上午1:03:41
 *配置权限解析标签的方法
 * 
 */

public class BeetlConfiguration extends BeetlGroupUtilConfiguration {
    @Override
    protected void initOther() {
        groupTemplate.registerFunctionPackage("panshiro", new ShiroExt());
    }
}

Remember that panshiro here will be useful for a while, and the shiroExt class here is the one above.

Finally add beetl's view resolver

<!-- 配置一个试图解析器ViewResolver(应用控制器) -->
	<bean name="beetlConfig" class="org.pan.shiro.shiro.BeetlConfiguration" init-method="init">
		<property name="configFileResource" value="classpath:beetl.properties" />
	</bean>
	<!-- Beetl视图解析器1 -->
	<bean name="beetlViewResolver" class="org.beetl.ext.spring.BeetlSpringViewResolver">
		<property name="suffix" value="" />
		<property name="contentType" value="text/html;charset=UTF-8" />
		<property name="order" value="0" />
		<!-- 多GroupTemplate,需要指定使用的bean -->
		<property name="config" ref="beetlConfig" />
	</bean>

The org.pan.shiro.shiro.BeetlConfiguration class here is the beetlconfig class written above.

How to use it, see this code

<% if(panshiro.hasPermission("admin")){%><a href="${ctxPath}/user/admin">退出</a><%}%>

The panshiro here is the panshiro we just matched. Whatever you write there, write it here. The meaning of this code is that if the login person has admin privileges, the a tag inside will be displayed. As for the hasPermisson method here, it is the method in shiroExt, which can be replaced by one of the methods. The meaning is the same as the comment inside

Well, this way, people who don't have the appropriate permissions will not be able to see related things on the page.

But there is still a problem. With the continuous improvement of the function, you may need to control more and more permissions, and some are even accurate to the button. However, do we still configure it in the above configuration file. That would be cumbersome, and the config file would get huge and not good for development. Now you need to enable shiro's annotation function. In spring-servlet.xml, note that it is not in spring-context.xml! Add the following configuration about shiro

<!--========================-如果使用注解方式验证将下面代码放开,记住因为是访问controller方法因此这段配置要放在视图控制器的配置文件中=============================== -->
	<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<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>
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--如果没有注解指定的权限,则调到下面指定的页面-->
                <prop key="org.apache.shiro.authz.UnauthorizedException">
                    redirect:/user/login
                </prop>
            </props>
        </property>
    </bean>

This way you can use shiro's related insights in the methods in the controller. There are five annotations for permissions:

@RequiresAuthentication: When a class, instance, or method annotated with this annotation is accessed or called, the current Subject must be authenticated in the current session.

@RequiresGuest: When a class, instance, or method marked with this annotation is accessed or called, the current Subject can be the "gust" identity, which does not need to be authenticated or has a record in the original session.

@RequiresPermissions: The method annotated with this annotation can only be executed when the current Subject needs to have some specific permissions. If the current Subject does not have such permission, the method will not be executed.

@RequiresRoles: Only when the current Subject must have all the specified roles can access the methods annotated with this annotation. If the Subject does not have all the specified roles at the same time on that day, the method will not be executed and an AuthorizationException will be thrown.

@RequiresUser: The current Subject must be the user of the application to access or call the classes, instances, and methods annotated with this annotation.

complete

Guess you like

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