Shiro(权限控制)

概述:

1、权限控制的两种方式:粗粒度基于URL级别权限控制、细粒度基于方法级别权限控制

2、基于Apache Shiro实现登录认证和权限控制,重点shiro权限控制流程、自定义Realm对象控制系统认证和授权

4、动态系统菜单显示功能

5、对认证和授权数据进行缓存优化;


权限控制的两种方式:

一、粗粒度URL级别权限控制:

可以基于Filter实现:在数据库中存放用户、权限、访问URL对应关系,当前用户访问一个URL地址,查询数据库判断用户当前具有权限,是否包含这个URL,如果包含允许访问,如果不包含则权限不足

二、细粒度方法级别权限控制

可以代理、自定义注解实现,访问目标对象方法,在方法上添加权限注解信息,对目标对象创建代理对象,访问真实对象先访问代理对象,在代理对象查询数据库判断是否具有注解上描述需要权限,具有权限允许访问,不具有权限,拦截访问,提示权限不足。


权限相关数据表分析和创建(能实现)

1、准备工作

1)用户(User):系统登录用户

2)权限(Permission):描述权限信息(粗粒度权限控制,在权限表描述访问资源URL信息)

3)角色(Role):方便用户进行授权,角色就是权限的集合

用户 *---* 角色 *---* 权限(5张表)

4)Menu菜单,为了方便进行动态菜单管理,为不同用户定制不同系统菜单

不同用户系统菜单,可以根据用户角色进行管理 角色*---*菜单(2张表)


ApacheShiro简介和项目导入  官网:http://shiro.apache.org/

Apache Shiro(更轻量,使用更简单,并不完全依赖spring,可以独立使用)

Apache Shiro体系结构:

  1. Authentication认证:用户登录,身份识别  who are you?
  2. Authorization 授权:用户具有哪些权限、角色 what can you do?
  3. Cryptography安全数据加密
  4. Session Management 会话管理
  5. Web Integration  web系统集成
  6. Integrations 集成其他应用,spring、缓存框架

ApacheShiro运行流程和权限控制方式分析:

shiro主要有三大功能模块:

1)Subject:    主体,一般指用户 ,就是shiro管理的用户(获取登录用户信息)

2)SecurityManager安全管理器,就是shiro权限控制核心对象,在编程时,只需要操作Subject方法,底层调用SecurityManager方法,无需直接编程操作SecurityManager   管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)

3)Realm  :用于进行权限信息的验证,一般需要自己实现。

2、Shiro执行流程:

应用程序--->Subject--->SecurityManager--->Realm--->安全数据

3、Shiro进行权限控制的四种主要方式:

1)在程序中通过Subject编程方式进行权限控制

2)配置Filter实现URL级别粗粒度权限控制,url级别

3)配置代理,基于注解实现细粒度权限控制

4)在页面中使用shiro自定义标签实现,页面显示权限控制


SSM整合Shiro

配置ShiroFilter实现URL级别权限控制:

  1. 配置web.xml
<!-- shiro的Filter  -->
	<filter> 
		<!-- 去spring配置文件中寻找同名bean  -->
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

2、配置applicationContext-shiro.xml

applicationContext.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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa 
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
 
	<!-- 扫描 @Server @Controller @Repository -->
	<context:component-scan base-package="cn.itcast"/>

	<!-- 加载properties文件 -->
	<context:property-placeholder location="classpath:config.properties" />
	
	<!-- 引入外部数据文件 -->
	<import resource="applicationContext-dataSource.xml"/>
	
	<!-- 引入WebService配置 -->
	<import resource="applicationContext-webService.xml"/>
	
	<!-- 引入quartz配置 -->
	<import resource="applicationContext-quartz.xml"/>
	
	<!-- 引入 mq配置  -->
	<import resource="applicationContext-mq.xml"/>
	
	<!-- 引入 elasticsearch 配置  -->
	<import resource="applicationContext-elasticsearch.xml"/>
	
	<!-- 引入 shiro权限控制配置 -->
	<import resource="applicationContext-shiro.xml"/>
	
	<!-- 引入cache 配置 -->
	<import resource="applicationContext-cache.xml"/>
</beans>

applicationContext-shiro.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:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa 
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
	
	<!-- 配置Shiro核心Filter  --> 
	<bean id="shiroFilter" 
		class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 安全管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 未认证,跳转到哪个页面  -->
		<property name="loginUrl" value="/login.html" />
		<!-- 登录页面页面 -->
		<property name="successUrl" value="/index.html" />
		<!-- 认证后,没有权限跳转页面 -->
		<property name="unauthorizedUrl" value="/unauthorized.html" />
		<!-- shiro URL控制过滤器规则  -->
		<property name="filterChainDefinitions">
			<value>
				/login.html* = anon
				/user_login.action* = anon 
				/validatecode.jsp* = anon
				/css/** = anon
				/js/** = anon
				/images/** = anon
				/services/** = anon 
				/pages/base/courier.html* = perms[courier:list]
				/pages/base/area.html* = roles[base]
				/** = authc
			</value>
		</property>
	</bean>
	
	<!-- 安全管理器  -->
	<bean id="securityManager" 
		class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="bosRealm" />
		<property name="cacheManager" ref="shiroCacheManager" />
	</bean>
	
	<!-- 配置Realm -->
	<bean id="bosRealm" class="cn.itcast.bos.realm.BosRealm">
		<!-- 缓存区的名字 就是 ehcache.xml 自定义 cache的name -->
		<property name="authorizationCacheName" value="bos" />
	</bean>

	//生命周期处理器
	<bean id="lifecycleBeanPostProcessor"
		class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
		
	<!-- 开启shiro注解模式  -->
	<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>
	
</beans>

/login.html后面加上*是因为会有如下情况:

http://localhost:9001/bos_management/login.html;jsessionid=68A360BFE7413C7CEAE81E5069F1EE81

/**=authc放在最后

ehcache.xml(额外)

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="java.io.tmpdir"/>

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
    
    <cache name="bos"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
    
    <cache name="standard"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
    
</ehcache>

Shiro过滤器配置参考:

anon    未认证可以访问

authc   认证后可以访问

perms  需要特定权限才能访问

roles    需要特定角色才能访问

user     需要特定用户才能访问

port     需要特定端口才能访问(不常用)

rest     根据指定HTTP请求才能访问(不常用)


自定义Realm实现用户登录功能:(登录成功保存用户信息)

1.提供login登录方法:

public String login() {
		// 用户名和密码 都保存在model中
		// 基于shiro实现登录
		Subject subject = SecurityUtils.getSubject();

		// 用户名和密码信息
		AuthenticationToken token = new UsernamePasswordToken(
				model.getUsername(), model.getPassword());
		try {
			subject.login(token);
			// 登录成功
			// 将用户信息 保存到 Session
			return SUCCESS;//跳转成功页面
		} catch (AuthenticationException e) {
			// 登录失败
			e.printStackTrace();
			return LOGIN;//重定向到登录页面
		}
	}

	
	public String logout() {
		// 基于shiro完成退出
		Subject subject = SecurityUtils.getSubject();
		subject.logout();

		return SUCCESS;
	}

Shiro执行流程:应用程序--->Subject--->SecurityManager--->Realm--->安全数据

 

2. 自定义Realm对象,实现认证方法(实际开发中,只需要继承AuthorizingRealm)

package cn.itcast.bos.realm;

import java.util.List;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import cn.itcast.bos.domain.system.Permission;
import cn.itcast.bos.domain.system.Role;
import cn.itcast.bos.domain.system.User;
import cn.itcast.bos.service.system.PermissionService;
import cn.itcast.bos.service.system.RoleService;
import cn.itcast.bos.service.system.UserService;

// 自定义Realm ,实现安全数据 连接
// @Service("bosRealm")
public class BosRealm extends AuthorizingRealm {

	@Autowired
	private UserService userService;

	@Autowired
	private RoleService roleService;

	@Autowired
	private PermissionService permissionService;

	@Override
	// 授权...
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
		System.out.println("shiro 授权管理...");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		// 根据当前登录用户 查询对应角色和权限
		Subject subject = SecurityUtils.getSubject();
		User user = (User) subject.getPrincipal();
		// 调用业务层,查询角色
		List<Role> roles = roleService.findByUser(user);
		for (Role role : roles) {
			authorizationInfo.addRole(role.getKeyword());
		}
		// 调用业务层,查询权限
		List<Permission> permissions = permissionService.findByUser(user);
		for (Permission permission : permissions) {
			authorizationInfo.addStringPermission(permission.getKeyword());
		}

		return authorizationInfo;
	}

	@Override
	// 认证...
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		System.out.println("shiro 认证管理... ");

		// 转换token
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;

		// 根据用户名 查询 用户信息
		User user = userService.findByUsername(usernamePasswordToken
				.getUsername());
		if (user == null) {
			// 用户名不存在
			// 参数一: 期望登录后,保存在Subject中信息
			// 参数二: 如果返回为null 说明用户不存在,报用户名
			// 参数三 :realm名称
			return null;
		} else {
			// 用户名存在
			// 当返回用户密码时,securityManager安全管理器,自动比较返回密码和用户输入密码是否一致
			// 如果密码一致 登录成功, 如果密码不一致 报密码错误异常
			return new SimpleAuthenticationInfo(user, user.getPassword(),
					getName());
		}

	}

}

3.在shiroFilter配置中将user_login.action放行

6、注意问题

1)当用户名不存在时,抛出异常

      2)当密码错误时,抛出异常

      


对页面访问添加授权控制(重要)

3.修改applicationContext-shiro.xml配置shiroFilter权限过滤程序

4.访问courier.html时,因为需要特别权限,调用自定义Realm的doGetAuthorizationInfo进行授权,如果没有权限,跳转


自定义Realm实现用户授权功能:

1、在数据表中导入预定义角色、权限数据,导入t_user_role_permission.sql

1)t_permission权限数据

2)t_role角色数据

3)t_role_permission角色权限关系表

4)t_user用户表

5)t_user_role用户角色关系表

2、实现Realm的授权方法


	@Override
	// 授权...
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
		System.out.println("shiro 授权管理...");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		// 根据当前登录用户 查询对应角色和权限
		Subject subject = SecurityUtils.getSubject();
		User user = (User) subject.getPrincipal();
		// 调用业务层,查询角色
		List<Role> roles = roleService.findByUser(user);
		for (Role role : roles) {
			authorizationInfo.addRole(role.getKeyword());
		}
		// 调用业务层,查询权限
		List<Permission> permissions = permissionService.findByUser(user);
		for (Permission permission : permissions) {
			authorizationInfo.addStringPermission(permission.getKeyword());
		}

		return authorizationInfo;
	}

配置注解实现业务层方法级别权限控制:

自定义注解(加在方法上,在注解中描述需要权限信息),对目标业务对象创建代理对象,在代理方法中使用反射技术读取注解信息,获取需要权限,查询当前登录用户具有权限是否满足;

  1. 配置applicationContext-shiro.xml激活注解
<!-- 开启shiro注解模式  -->
	<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>
  1. 配置快递员添加方法,业务层使用shiro注解

3、注意问题:

1)针对CourierServiceImpl对象进行代理,而不是针对接口进行代理

解决:

applicationContext-dataSource.xml

applicationContext-shiro.xml

2)使用方法注解进行权限控制,当权限不足时,代理对象抛出一个异常

21:53:00,549 ERROR DefaultDispatcherErrorHandler:42 - Exception occurred during processing request: Subject does not have permission [courier:add]

org.apache.shiro.authz.UnauthorizedException: Subject does not have permission [courier:add]

Caused by: org.apache.shiro.authz.AuthorizationException: Not authorized to invoke method: public void cn.itcast.bos.service.base.impl.CourierServiceImpl.save(cn.itcast.bos.domain.base.Courier)

at org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor.assertAuthorized(AuthorizingAnnotationMethodInterceptor.java:90)

... 122 more


通过shiro自定义标签控制页面元素显示(控制功能按钮的权限):

在页面引入shiro标签库

通过shiro自带标签控制按钮是否显示


三、shiro几种权限控制方式小结:

第一种:URL级别粗粒度权限控制(重点)

  • 配置web.xml的shiroFilter拦截 /*
  • 在spring的applicationContext*.xml配置文件中配置同名的bean,配置filterChainDefinitions拦截控制规则
  • xxx.html*=anon(未登录可以访问)
  • xxx.html*=authc(必须登录才能访问)
  • xxx.html*=perms[权限](需要特定权限才能访问 )
  • xxx.html*=roles[角色](需要特定角色才能访问)

第二种:方法级别细粒度权限控制:

在spring的applicationContext*.xml配置spring aop对spring管理bean对象开启shiro注解支持

@RequiresPermissions(权限) 需要特定权限才能访问

@RequiresRoles(角色) 需要特定角色才能访问

@RequiresAuthentication  需要认证才能访问

第三种:通过shiro自定义标签,实现页面元素显示控制:

<shiro:authenticated> 登录后才能访问

<shiro:hasPermission name=”abc”> 需要特定权限才能访问

<shior:hasRole name=”abc”> 需要特定角色才能访问

第四种:在程序中通过代码,判断用户是否具有指定权限(不太常用,有代码侵入)

补充:权限表达式“:”代表子权限

权限courier包含courier:add、courier:list、courier:*,如果用户具有父权限,操作所有子权限功能

比如:权限courier:list 包含courier:list:10


基础介绍:https://www.cnblogs.com/tohxyblog/p/6870261.html


Springboot整合Shiro:

pom文件:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.0.1</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>

user.java

package com.wsl.bean;

import java.util.Set;

public class User {
    private String id;
    private String userName;
    private String password;
    /**
     * 用户对应的角色集合
     */
    private Set<Role> roles;
    //省略set、get方法等.....
}

Role.java(角色对应实体类):
 

package com.wsl.bean;

import java.util.Set;

public class Role {

    private String id;
    private String roleName;
    /**
     * 角色对应权限集合
     */
    private Set<Permissions> permissions;
    //省略set、get方法等.....
}

Permissions.java(权限对应实体类):

public class Permissions {
    private String id;
    private String permissionsName;
    //省略set、get方法等.....
}

LoginServiceImpl.java:

package com.wsl.service.impl;

import com.wsl.bean.Permissions;
import com.wsl.bean.Role;
import com.wsl.bean.User;
import com.wsl.service.LoginService;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Service
public class LoginServiceImpl implements LoginService {

    @Override
    public User getUserByName(String getMapByName) {
        //模拟数据库查询,正常情况此处是从数据库或者缓存查询。
        return getMapByName(getMapByName);
    }

    /**
     * 模拟数据库查询
     * @param userName
     * @return
     */
    private User getMapByName(String userName){
        //共添加两个用户,两个用户都是admin一个角色,
        //wsl有query和add权限,zhangsan只有一个query权限
        Permissions permissions1 = new Permissions("1","query");
        Permissions permissions2 = new Permissions("2","add");
        Set<Permissions> permissionsSet = new HashSet<>();
        permissionsSet.add(permissions1);
        permissionsSet.add(permissions2);
        Role role = new Role("1","admin",permissionsSet);
        Set<Role> roleSet = new HashSet<>();
        roleSet.add(role);
        User user = new User("1","wsl","123456",roleSet);
        Map<String ,User> map = new HashMap<>();
        map.put(user.getUserName(), user);

        Permissions permissions3 = new Permissions("3","query");
        Set<Permissions> permissionsSet1 = new HashSet<>();
        permissionsSet1.add(permissions3);
        Role role1 = new Role("2","user",permissionsSet1);
        Set<Role> roleSet1 = new HashSet<>();
        roleSet1.add(role1);
        User user1 = new User("2","zhangsan","123456",roleSet1);
        map.put(user1.getUserName(), user1);
        return map.get(userName);
    }
}

自定义Realm用于查询用户的角色和权限信息并保存到权限管理器:

CustomRealm.java

package com.wsl.shiro;

import com.wsl.bean.Permissions;
import com.wsl.bean.Role;
import com.wsl.bean.User;
import com.wsl.service.LoginService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录用户名
        String name = (String) principalCollection.getPrimaryPrincipal();
        //根据用户名去数据库查询用户信息
        User user = loginService.getUserByName(name);
        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for (Role role : user.getRoles()) {
            //添加角色
            simpleAuthorizationInfo.addRole(role.getRoleName());
            //添加权限
            for (Permissions permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
            }
        }
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //加这一步的目的是在Post请求的时候会先进认证,然后在到请求
        if (authenticationToken.getPrincipal() == null) {
            return null;
        }
        //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
        User user = loginService.getUserByName(name);
        if (user == null) {
            //这里返回后会报出对应异常
            return null;
        } else {
            //这里验证authenticationToken和simpleAuthenticationInfo的信息
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
            return simpleAuthenticationInfo;
        }
    }
}

把CustomRealm和SecurityManager等加入到spring容器:

ShiroConfig.java:

package com.wsl.config;

import com.wsl.shiro.CustomRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class shiroConfig {
    //不加这个注解不生效,具体不详
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    //将自己的验证方式加入容器
    @Bean
    public CustomRealm myShiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        //登出
        map.put("/logout", "logout");
        //对所有用户认证
        map.put("/**", "authc");
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    //加入注解的使用,不加入这个注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

LoginController.java:

package com.wsl.controller;

import com.wsl.bean.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

    @RequestMapping("/login")
    public String login(User user) {
        //添加用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUserName(),
                user.getPassword()
        );
        try {
            //进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
//            subject.checkRole("admin");
//            subject.checkPermissions("query", "add");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            e.printStackTrace();
            return "没有权限";
        }
        return "login success";
    }
     //注解验角色和权限
    @RequiresRoles("admin")
    @RequiresPermissions("add")
    @RequestMapping("/index")
    public String index() {
        return "index!";
    }
}

注解验证角色和权限的话无法捕捉异常,从而无法正确的返回给前端错误信息,所以我加了一个类用于拦截异常,具体代码如下

MyExceptionHandler.java

package com.wsl.filter;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
@Slf4j
public class MyExceptionHandler {

    @ExceptionHandler
    @ResponseBody
    public String ErrorHandler(AuthorizationException e) {
        log.error("没有通过权限验证!", e);
        return "没有通过权限验证!";
    }
}

 

 

 

 

 

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值