1、权限、角色的认知
权限:能够做什么,可以操作的功能(能访问的页面,能提交的请求)
一个系统,有很多功能,每个人因为工作职责不同,需要有不同的权限
一个系统就有很多的权限,某些人具有相同的权限,为了便于权限的分配,引入了角色的概念
主管角色:50个权限
给某类人,分配角色即可
认证:登录,对身份的认知
2、认证、权限的框架的认知
三大模块:
1、Subject:用户,每一个登录的用户
2、Security Manager :核心,包含 认证、权限、session管理、加密、记住我、缓存
3、Realm: 登录、权限的访问(数据库)
Security Manager的组成
-
Authentication:身份认证/登录(账号密码验证)。
-
Authorization:授权,即角色或者权限验证。
-
Session Manager:会话管理,用户登录后的session相关管理。
-
Cryptography:加密,密码加密等。
-
Web Support:Web支持,集成Web环境。
-
Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
-
Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
-
Testing:测试支持;
-
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
-
Remember Me:记住我,登录后,下次再来的话不用登录了。
【原理】Shiro的核心原理:过滤器拦截技术
3、 Shiro的使用
1、在SSM框架下使用
2、添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
lombok简化实体类的一个框架,使用lombok后,实体类只要写属性即可,无需写构造器、getXXX setXXX方法
【注意】使用lombok除了添加依赖之外,还需要在IDEA中安装lombok插件
file--->settings---->plugins---->搜索Lombok install即可
3、创建表
4、创建实体类
5、编写登录的dao
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.ssm2105.dao.UserDao">
<select id="login" parameterType="String" resultType="User">
select id,nickname,pwd,email,userid
from u_user
where userid=#{userId}
</select>
</mapper>
6、编写Realm,在Realm中调用dao
编写认证方法
//认证方法
//登录,获取用户信息
//参数token里面存放了登录的账号以及密码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//判断是否输入账号
if(StringUtils.isEmpty(token.getPrincipal())){
//直接返回请求
return null;
}
//获取账号信息
String userId=token.getCredentials().toString();
//调用业务逻辑查询用户信息
User user=userService.login(userId);
//对查询得到的账户进行判断,如果是null则返回
if(user==null){
return null;
}else{
//进行密码验证
SimpleAuthenticationInfo simpleAuthenticationInfo=
new SimpleAuthenticationInfo(userId,user.getPwd(),getName());
//在验证密码的过程中,如果密码错误,则直接return null
// 如果密码正确,则进行授权,调用 doGetAuthorizationInfo()方法
return simpleAuthenticationInfo;
}
}
编写授权方法
先查询用户对应的权限:
5表联查
SELECT u.id,nickname,pwd,email,userid,r.id rid,r.`name` rname,
up.id pid,up.`name` pname,up.url
from u_user u
INNER JOIN u_user_role uur on u.id=uur.uid
INNER JOIN u_role r on uur.rid=r.id
INNER JOIN u_role_permission urp on uur.rid=urp.rid
INNER JOIN u_permission up on urp.pid=up.id
where userid='zhangsan'
mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.ssm2105.dao.UserDao">
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="nickName" column="nickname"/>
<result property="pwd" column="pwd"/>
<result property="email" column="email"/>
<result property="userId" column="userid"/>
<association property="roleSet" javaType="Role">
<id property="id" column="rid" ></id>
<result property="name" column="rname"/>
<association property="permissionList" javaType="Permission">
<id property="id" column="pid"/>
<result property="name" column="pname"/>
<result property="url" column="url"/>
</association>
</association>
</resultMap>
<select id="login" parameterType="String" resultMap="userMap">
SELECT u.id,nickname,pwd,email,userid,r.id rid,r.`name` rname,
up.id pid,up.`name` pname,up.url
from u_user u
INNER JOIN u_user_role uur on u.id=uur.uid
INNER JOIN u_role r on uur.rid=r.id
INNER JOIN u_role_permission urp on uur.rid=urp.rid
INNER JOIN u_permission up on urp.pid=up.id
where userid=#{userId}
</select>
</mapper>
授权方法
//授权方法
//查询当前登录用户的角色,以及该角色所对应的权限
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//一个用户有多个角色,每个角色有多个权限
//1、先获取用户信息
String userId=(String) principals.getPrimaryPrincipal();
//2、查询得到用户信息,权限、角色一并得到
User user=userService.login(userId);
//添加权限
//创建权限对象
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
//先获取角色
for(Role role:user.getRoleSet()){
//添加角色
simpleAuthorizationInfo.addRole(role.getName());
//再根据角色获取权限
for(Permission permission: role.getPermissionList()){
//添加获取到的权限
simpleAuthorizationInfo.addStringPermission(permission.getUrl());
}
}
return simpleAuthorizationInfo;
}
完整的Realm
package com.qf.ssm2105.realms;
import com.qf.ssm2105.pojo.Permission;
import com.qf.ssm2105.pojo.Role;
import com.qf.ssm2105.pojo.User;
import com.qf.ssm2105.service.UserService;
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;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class CustomRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权方法
//查询当前登录用户的角色,以及该角色所对应的权限
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//一个用户有多个角色,每个角色有多个权限
//1、先获取用户信息
String userId=(String) principals.getPrimaryPrincipal();
//2、查询得到用户信息,权限、角色一并得到
User user=userService.login(userId);
//添加权限
//创建权限对象
SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
//先获取角色
for(Role role:user.getRoleSet()){
//添加角色
simpleAuthorizationInfo.addRole(role.getName());
//再根据角色获取权限
for(Permission permission: role.getPermissionList()){
//添加获取到的权限
simpleAuthorizationInfo.addStringPermission(permission.getUrl());
}
}
return simpleAuthorizationInfo;
}
//认证方法
//登录,获取用户信息
//参数token里面存放了登录的账号以及密码
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//判断是否输入账号
if(StringUtils.isEmpty(token.getPrincipal())){
//直接返回请求
return null;
}
//获取账号信息
String userId=token.getCredentials().toString();
//调用业务逻辑查询用户信息
User user=userService.login(userId);
//对查询得到的账户进行判断,如果是null则返回
if(user==null){
return null;
}else{
//进行密码验证
SimpleAuthenticationInfo simpleAuthenticationInfo=
new SimpleAuthenticationInfo(userId,user.getPwd(),getName());
//在验证密码的过程中,如果密码错误,则直接return null
// 如果密码正确,则进行授权,调用 doGetAuthorizationInfo()方法
return simpleAuthenticationInfo;
}
}
}
配置---把Realm和SecurityManager整合在一起
在spring的配置文件中
<!--整合shrio-->
<!--创建密码匹配器-->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property>
<property name="hashIterations" value="1024"></property>
</bean>
<bean id="customRealm" class="com.qf.ssm2105.realms.CustomRealm">
<!-- <property name="credentialsMatcher" ref="credentialsMatcher"></property>-->
</bean>
<!--创建SecurityManager对象-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm"></property>
</bean>
<!--配置shiro过滤器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--进行认证、授权-->
<property name="securityManager" ref="securityManager"></property>
<!--对特殊的请求进行配置,相当于放行-->
<property name="loginUrl" value="/login.html"></property><!--非前后端分离-->
<!--当没有某个操作的权限时,shiro会抛出异常,只要抛出没有权限的异常,可以跳转到指定的页面-->
<property name="unauthorizedUrl" value="/unauthorized.html"></property>
<!--定义过滤规则 过滤器链:配置的顺序就是过滤的顺序-->
<property name="filterChainDefinitions">
<value>
<!--静态资源放行,直接找静态资源,不走:securityManager-->
/static/** =anon
/css/** =anon
/js/** =anon
/img/** =anon
/user/register =anon <!--对用户注册请求放行-->
/user/login =anon <!--对登录请求放行-->
<!--退出/登出 shiro清空当前用户的session-->
/logout =logout
<!--user:需要登录才能访问 /** 一定放在最后
请求都会到达securityManager,进行认证、授权及权限判断
-->
/** =user
</value>
</property>
</bean>
<!--aop思想进行权限的控制-->
<!--创建shiro的处理器:自动执行AuthorizationAttributeSourceAdvisor类型的增强处理-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!--创建springAop的代理对象,让代理对象执行shiro的处理器-->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"></property>
</bean>
<!--创建权限验证的增强处理-->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
配置web.xml文件,拦截请求
<!--拦截请求,把请求转移到Shiro的过滤器中-->
<!--
DelegatingFilterProxy对象到bean容器中找名字为shiroFilter的过滤器
把请求都交由名字为shiroFilter的bean进行处理
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4、登录功能的实现
package com.qf.ssm2105.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "*")
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
public String login(String userId,String pwd){
//Shiro实现登录
Subject subject= SecurityUtils.getSubject();
//把账号、密码封装在token里
UsernamePasswordToken token=new UsernamePasswordToken(userId,pwd);
try {
//登录请求
subject.login(token);
return "success";
}catch (UnknownAccountException e){
return "用户名不存在";
}catch (AuthenticationException e){
return "账号或秘密错误";
}catch (AuthorizationException e){
return "没有操作权限";
}
}
}
5、继续配置
在springMVC的配置文件中,配置权限的增强处理
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
6、Handler方法中加权限注解
package com.qf.ssm2105.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "*")
@RequestMapping("/dept1")
public class DeptController1 {
@GetMapping("/add")
@RequiresPermissions("dept1/add")
public String add(){
return "add";
}
@GetMapping("/update")
@RequiresPermissions("dept1/update")
public String update(){
return "update";
}
@GetMapping("/delete")
@RequiresPermissions("dept1/delete")
public String delete(){
return "delete";
}
@GetMapping("/findall")
@RequiresPermissions("dept1/findall")
public String findAll(){
return "findall";
}
}
【说明】如果没有权限,将抛出异常
7、总结
1、职能:认证、权限验证、记住我、加密、session管理等
2、使用时:5张表存储角色、权限
3、先编写Realm(登录、授权)
4、配置
spring的配置文件
---配置了realm的bean
---配置了securityManager的bean 引用realm的bean
---配置过滤器 anon---无需登录,直接访问(游客)
user---登录后才能访问,认证
authc---登录、授权后,有权限才能访问
---aop的代理实现权限的增强处理
springMVC配置文件
---配置权限的增强处理
web.xml文件
拦截住所有请求,拦截到shiro的过滤器中