不同身份的用户进入到系统所能够完成的操作是不相同的,我们对不同用户进行的可执行的操作的管理称之为权限管理。
![]() |
权限管理设计
基于主页的权限管理(不同用户使用不同的主页,权限通过主页功能菜单进行限制)
![]() |
基于用户和权限的权限管理
![]() |
基于角色的访问控制
![]() |
认证:对用户的身份进行检查(登录验证)
授权:对用户的权限进行检查(是否有对应的操作权限)
流程示意图:
Shiro:Apache Shiro
是一个功能强大并且易用的Java安全框架 (小而简单)Spring Security
:基于Spring的一个安全框架,依赖SpringOAuth2
:第三方授权登录Apache Shiro
是一个功能强大并且易用的Java安全框架Anthentication
认证,验证用户是否有相应的身份—登录认证;
Authorization
授权,即权限验证;对已经通过认证的用户检查是否具有某个权限或者角色,从而控制是否能够进行某种操作;
Session Managment
会话管理,用户在认证成功之后创建会话,在没有退出之前,当前用户的所有信息都会保存在这个会话中;可以是普通的JavaSE应用,也可以是web应用;
Cryptography
加密,对敏感信息进行加密处理,shiro就提供这种加密机制;
支持的特性:
Web Support
— Shiro提供了过滤器,可以通过过滤器拦截web请求来处理web应用的访问控制Caching
缓存支持,shiro可以缓存用户信息以及用户的角色权限信息,可以提高执行效率Concurrency
, shiro支持多线程应用Testing
提供测试支持Run As
允许一个用户以另一种身份去访问Remeber Me
说明:Shiro是一个安全框架,不提供用户、权限的维护(用户的权限管理需要我们自己去设计)
![]() |
Subject
、Security Manager
、Realms
Authenticator
,认证器Anthorizer
,授权器SessionManager
,会话管理器CacheManager
,缓存管理器![]() |
org.apache.shiro shiro-core 1.4.1
[users]
zhangsan=123456,seller
lisi=666666,ckmgr
admin=222222,admin[roles]
admin=*
seller=order-add,order-del,order-list
ckmgr=ck-add,ck-del,ck-list
package com.qfedu.shiro;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;import java.util.Scanner;public class TestShiro {public static void main(String[] args) {Scanner scan = new Scanner(System.in);System.out.println("请输入帐号:");String username = scan.nextLine();System.out.println("请输入密码:");String password = scan.nextLine();//1.创建安全管理器DefaultSecurityManager securityManager = new DefaultSecurityManager();//2.创建realmIniRealm iniRealm = new IniRealm("classpath:shiro.ini");//3.将realm设置给安全管理器securityManager.setRealm(iniRealm);//4.将Realm设置给SecurityUtils工具SecurityUtils.setSecurityManager(securityManager);//5.通过SecurityUtils工具类获取subject对象Subject subject = SecurityUtils.getSubject();//【认证流程】//a.将认证帐号和密码封装到token对象中UsernamePasswordToken token = new UsernamePasswordToken(username,password);//b.通过subject对象调用login方法进行认证申请:boolean b = false;try{subject.login(token);b = true;}catch(IncorrectCredentialsException e){b = false;}System.out.println(b?"登录成功":"登录失败");//【授权】//判断是否有某个角色System.out.println(subject.hasRole("seller"));//判断是否有某个权限boolean permitted = subject.isPermitted("order-del");System.out.println(permitted);}}
1.导入依赖
org.apache.shiro shiro‐core 1.4.1
org.apache.shiro shiro‐web 1.4.1
org.apache.shiro shiro‐spring 1.4.1
2.在web.xml中添加shrio对应的过滤器配置
拦截所有请求
contextConfigLocationclasspath:spring‐*.xml org.springframework.web.context.ContextLoaderListener springMVC org.springframework.web.servlet.DispatcherServlet contextConfigLocationclasspath:spring‐context*.xml springMVC *.do shiroFilter org.springframework.web.filter.DelegatingFilterProxy targetFilterLifecycletrue shiroFilter /*
3.在spring配置文件中,配置shrio框架所需的内容
‐‐>/**=anon
4.配置权限验证使用注解
1.创建用户权限表,并添加数据
因为此案例目前使用的是shiro默认数据库结构,则需要按照shiro默认的数据表结构进行创建及配置
用户信息
create table users(
id int primary key auto_increment,
username varchar( 60 ) not null,
password varchar( 60 ) not null,password_salt varchar( 100 )
);
添加用户信息
insert into users(username,password)
values('zhangsan','e10adc3949ba59abbe56e057f20f883e');
insert into users(username,password)
values('lisi','e10adc3949ba59abbe56e057f20f883e');
insert into users(username,password)
values('wangwu','e10adc3949ba59abbe56e057f20f883e');
insert into users(username,password)
values('zhaoliu','e10adc3949ba59abbe56e057f20f883e');
insert into users(username,password)
values('chenqi','e10adc3949ba59abbe56e057f20f883e');
create table user_roles(
id int primary key auto_increment,
username varchar( 60 ) not null,
role_name varchar( 100 ) not null
);‐‐ 添加用户角色
insert into user_roles(username,role_name) values('zhangsan','admin');
insert into user_roles(username,role_name) values('lisi','user');
insert into user_roles(username,role_name) values('wangwu','cmanager');
insert into user_roles(username,role_name) values('zhaoliu','mmanager');
insert into user_roles(username,role_name) values('chenqi','xmanager');
create table roles_permissions(
id int primary key auto_increment,
role_name varchar( 100 ) not null,
permission varchar( 100 ) not null
);‐‐ 添加权限信息
insert into roles_permissions(role_name,permission) values('admin','*');
insert into roles_permissions(role_name,permission) values('user','sys:*:find');
insert into roles_permissions(role_name,permission) values('cmanager','sys:c:save');
insert into roles_permissions(role_name,permission) values('cmanager','sys:c:delete');
insert into roles_permissions(role_name,permission) values('cmanager','sys:c:update');
insert into roles_permissions(role_name,permission) values('cmanager','sys:c:find');
insert into roles_permissions(role_name,permission) values('mmanager','sys:m:save');
insert into roles_permissions(role_name,permission) values('mmanager','sys:m:delete');
insert into roles_permissions(role_name,permission) values('mmanager','sys:m:update');
insert into roles_permissions(role_name,permission) values('mmanager','sys:m:find');
insert into roles_permissions(role_name,permission) values('xmanager','sys:x:save');
insert into roles_permissions(role_name,permission) values('xmanager','sys:x:delete');
insert into roles_permissions(role_name,permission) values('xmanager','sys:x:update');
insert into roles_permissions(role_name,permission) values('xmanager','sys:x:find');
@Service
public class UserService {
public void login(String userName,String userPwd) throws Exception{Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);subject.login(token);}
}
@Controller
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login.do")public String login(String userName, String userPwd, Model model){try{userService.login(userName,userPwd);System.out.println("‐‐‐‐success");return "index.jsp";}catch (Exception e){System.out.println("‐‐‐‐fail");model.addAttribute("tips","登录验证失败,请重试!");return "login.jsp";}}
}
<%@ page contentType="text/html;charset=UTF‐8" language="java" %>
Title ${tips}
- index.jsp
<%@ page contentType="text/html;charset=UTF‐8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
Title ,欢迎您![用户角色:admin user cmanager mmanager xmanager ]
C模块- 添加C
- 删除C
- 修改C
- 查询C
X模块- 添加X
- 删除X
- 修改X
- 查询X
M模块- 添加M
- 删除M
- 修改M
- 查询M
依赖
com.alibaba druid-spring-boot-starter 1.1.10
mysql mysql-connector-java 5.1.47
org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.0
配置
spring:datasource:druid:url: jdbc:mysql://47.96.11.185:3306/test# MySQL如果是8.x com.mysql.cj.jdbc.Driverdriver-class-name: com.mysql.jdbc.Driverusername: rootpassword: admin123initial-size: 1min-idle: 1max-active: 20
mybatis:mapper-locations: classpath:mappers/*Mapper.xmltype-aliases-package: com.qfedu.springbootssm.beans
导入依赖
org.apache.shiro shiro-spring 1.4.1
Shiro配置(java配置方式)
@Configuration
public class ShiroConfig {@Beanpublic IniRealm getIniRealm(){IniRealm iniRealm = new IniRealm("classpath:shiro.ini");return iniRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(IniRealm iniRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//securityManager要完成校验,需要realmsecurityManager.setRealm(iniRealm);return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的filter.setSecurityManager(securityManager);//设置shiro的拦截规则// anon 匿名用户可访问// authc 认证用户可访问// user 使用RemeberMe的用户可访问// perms 对应权限可访问// role 对应的角色可访问Map filterMap = new HashMap<>();filterMap.put("/","anon");filterMap.put("/login.html","anon");filterMap.put("/regist.html","anon");filterMap.put("/user/login","anon");filterMap.put("/user/regist","anon");filterMap.put("/static/**","anon");filterMap.put("/**","authc");filter.setFilterChainDefinitionMap(filterMap);filter.setLoginUrl("/login.html");//设置未授权访问的页面路径filter.setUnauthorizedUrl("/login.html");return filter;}}
认证测试
@Service
public class UserServiceImpl {public void checkLogin(String userName,String userPwd) throws Exception{Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);subject.login(token);}}
@Controller
@RequestMapping("user")
public class UserController {@Resourceprivate UserServiceImpl userService;@RequestMapping("login")public String login(String userName,String userPwd){try {userService.checkLogin(userName,userPwd);System.out.println("------登录成功!");return "index";} catch (Exception e) {System.out.println("------登录失败!");return "login";}}
}
login.html
Title login
![]() |
![]() |
用户信息表: users
create table users(id int primary key auto_increment,username varchar(60) not null unique,password varchar(20) not null,password_salt varchar(20)
);insert into users(username,password) values('zhangsan','123456');
insert into users(username,password) values('lisi','123456');
insert into users(username,password) values('wangwu','123456');
insert into users(username,password) values('zhaoliu','123456');
insert into users(username,password) values('chenqi','123456');
角色信息表: user_roles
create table user_roles(id int primary key auto_increment,username varchar(60) not null,role_name varchar(100) not null
);-- admin系统管理员
-- cmanager 库管人员
-- xmanager 销售人员
-- kmanager 客服人员
-- zmanager 行政人员
insert into user_roles(username,role_name) values('zhangsan','admin');
insert into user_roles(username,role_name) values('lisi','cmanager');
insert into user_roles(username,role_name) values('wangwu','xmanager');
insert into user_roles(username,role_name) values('zhaoliu','kmanager');
insert into user_roles(username,role_name) values('chenqi','zmanager');
权限信息表:roles_permissions
create table roles_permissions(id int primary key auto_increment,role_name varchar(100) not null,permission varchar(100) not null
);-- 权限 sys:c:save sys:c:delete...
-- 管理员具备所有权限
insert into roles_permissions(role_name,permission) values("admin","*");
-- 库管人员
insert into roles_permissions(role_name,permission) values("cmanager","sys:c:save");
insert into roles_permissions(role_name,permission) values("cmanager","sys:c:delete");
insert into roles_permissions(role_name,permission) values("cmanager","sys:c:update");
insert into roles_permissions(role_name,permission) values("cmanager","sys:c:find");
-- 销售人员
insert into roles_permissions(role_name,permission) values("xmanager","sys:c:find");
insert into roles_permissions(role_name,permission) values("xmanager","sys:x:save");
insert into roles_permissions(role_name,permission) values("xmanager","sys:x:delete");
insert into roles_permissions(role_name,permission) values("xmanager","sys:x:update");
insert into roles_permissions(role_name,permission) values("xmanager","sys:x:find");insert into roles_permissions(role_name,permission) values("xmanager","sys:k:save");
insert into roles_permissions(role_name,permission) values("xmanager","sys:k:delete");
insert into roles_permissions(role_name,permission) values("xmanager","sys:k:update");
insert into roles_permissions(role_name,permission) values("xmanager","sys:k:find");
-- 客服人员
insert into roles_permissions(role_name,permission) values("kmanager","sys:k:find");
insert into roles_permissions(role_name,permission) values("kmanager","sys:k:update");
-- 新增人员
insert into roles_permissions(role_name,permission) values("zmanager","sys:*:find");
创建SpringBoot应用
整合Druid和MyBatis
整合shiro
添加依赖
org.apache.shiro shiro-spring 1.4.1
配置Shiro
@Configuration
public class ShiroConfig {@Beanpublic JdbcRealm getJdbcRealm(DataSource dataSource){JdbcRealm jdbcRealm = new JdbcRealm();//JdbcRealm会自行从数据库查询用户及权限数据(数据库的表结构要符合JdbcRealm的规范)jdbcRealm.setDataSource(dataSource);//JdbcRealm默认开启认证功能,需要手动开启授权功能jdbcRealm.setPermissionsLookupEnabled(true);return jdbcRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(JdbcRealm jdbcRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(jdbcRealm);return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的filter.setSecurityManager(securityManager);Map filterMap = new HashMap<>();filterMap.put("/","anon");filterMap.put("/login.html","anon");filterMap.put("/regist.html","anon");filterMap.put("/user/login","anon");filterMap.put("/user/regist","anon");filterMap.put("/static/**","anon");filterMap.put("/**","authc");filter.setFilterChainDefinitionMap(filterMap);filter.setLoginUrl("/login.html");//设置未授权访问的页面路径filter.setUnauthorizedUrl("/login.html");return filter;}}
略
当用户认证进入到主页面之后,需要显示用户信息及当前用户的权限信息;Shiro就提供了一套标签用于在页面来进行权限数据的呈现
Shiro提供了可供JSP使用的标签以及Thymeleaf中标签
JSP页面中引用:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
Thymeleaf模版中引用:
com.github.theborakompanioni thymeleaf-extras-shiro 2.0.0
@Configuration
public class ShiroConfig {@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}//...
}
...
常用标签
guest,判断用户是否是游客身份,如果是游客身份则显示此标签内容
欢迎游客访问,登录
user,判断用户是否是认证身份,如果是认证身份则显示此标签内容
principal,获取当前登录用户名
用户[ ]欢迎您!
notAuthenticated/authenticated
hasRole
hasPermission
Title
index
欢迎游客访问,登录 用户[ ]欢迎您!当前用户为超级管理员 仓管人员 销售人员 客服人员 行政人员
仓库管理订单管理客户管理
使用JdbcRealm可以完成用户权限管理,但是我们必须提供JdbcRealm规定的数据表结构;如果在我们的项目开发中 ,这个JdbcRealm规定的数据表结构不能满足开发需求,该如何处理呢?
- 自定义数据库表结构
- 自定义Realm实现认证和授权
![]() |
RBAC基于角色的访问控制
-- 用户信息表
create table tb_users(user_id int primary key auto_increment,username varchar(60) not null unique,password varchar(20) not null,password_salt varchar(60)
);insert into tb_users(username,password) values('zhangsan','123456');
insert into tb_users(username,password) values('lisi','123456');
insert into tb_users(username,password) values('wangwu','123456');
insert into tb_users(username,password) values('zhaoliu','123456');
insert into tb_users(username,password) values('chenqi','123456');-- 角色信息表
create table tb_roles(role_id int primary key auto_increment,role_name varchar(60) not null
);insert into tb_roles(role_name) values('admin');
insert into tb_roles(role_name) values('cmanager'); -- 仓管
insert into tb_roles(role_name) values('xmanager'); -- 销售
insert into tb_roles(role_name) values('kmanager'); -- 客服
insert into tb_roles(role_name) values('zmanager'); -- 行政-- 权限信息表
create table tb_permissions(permission_id int primary key auto_increment, -- 1permission_code varchar(60) not null, -- sys:c:findpermission_name varchar(60) -- 仓库查询
);
insert into tb_permissions(permission_code,permission_name) values('sys:c:save','入库');
insert into tb_permissions(permission_code,permission_name) values('sys:c:delete','出库');
insert into tb_permissions(permission_code,permission_name) values('sys:c:update','修改');
insert into tb_permissions(permission_code,permission_name) values('sys:c:find','查询');insert into tb_permissions(permission_code,permission_name) values('sys:x:save','新增订单');
insert into tb_permissions(permission_code,permission_name) values('sys:x:delete','删除订单');
insert into tb_permissions(permission_code,permission_name) values('sys:x:update','修改订单');
insert into tb_permissions(permission_code,permission_name) values('sys:x:find','查询订单');insert into tb_permissions(permission_code,permission_name) values('sys:k:save','新增客户');
insert into tb_permissions(permission_code,permission_name) values('sys:k:delete','删除客户');
insert into tb_permissions(permission_code,permission_name) values('sys:k:update','修改客户');
insert into tb_permissions(permission_code,permission_name) values('sys:k:find','查询客户');-- 用户角色表
create table tb_urs(uid int not null,rid int not null-- primary key(uid,rid),-- constraint FK_user foreign key(uid) references tb_users(user_id),-- constraint FK_role foreign key(rid) references tb_roles(role_id)
);
insert into tb_urs(uid,rid) values(1,1);
insert into tb_urs(uid,rid) values(1,2);
insert into tb_urs(uid,rid) values(1,3);
insert into tb_urs(uid,rid) values(1,4);
insert into tb_urs(uid,rid) values(1,5);insert into tb_urs(uid,rid) values(2,2);
insert into tb_urs(uid,rid) values(3,3);
insert into tb_urs(uid,rid) values(4,4);
insert into tb_urs(uid,rid) values(5,5);-- 角色权限表
create table tb_rps(rid int not null,pid int not null
);
-- 给仓管角色分配权限
insert into tb_rps(rid,pid) values(2,1);
insert into tb_rps(rid,pid) values(2,2);
insert into tb_rps(rid,pid) values(2,3);
insert into tb_rps(rid,pid) values(2,4);
-- 给销售角色分配权限
insert into tb_rps(rid,pid) values(3,4);
insert into tb_rps(rid,pid) values(3,5);
insert into tb_rps(rid,pid) values(3,6);
insert into tb_rps(rid,pid) values(3,7);
insert into tb_rps(rid,pid) values(3,8);
insert into tb_rps(rid,pid) values(3,9);
insert into tb_rps(rid,pid) values(3,10);
insert into tb_rps(rid,pid) values(3,11);
insert into tb_rps(rid,pid) values(3,12);
-- 给客服角色分配权限
insert into tb_rps(rid,pid) values(4,11);
insert into tb_rps(rid,pid) values(4,12);
-- 给行政角色分配权限
insert into tb_rps(rid,pid) values(5,4);
insert into tb_rps(rid,pid) values(5,8);
insert into tb_rps(rid,pid) values(5,12);
Shiro进行认证需要用户信息:
- 根据用户名查询用户信息
Shiro进行授权管理需要当前用户的角色和权限
根据用户名查询当前用户的角色列表(3张表连接查询)
根据用户名查询当前用户的权限列表(5张表连接查询)
@Data
public class User {private Integer userId;private String userName;private String userPwd;private String pwdSalt;
}
public interface UserDAO {public User queryUserByUsername(String username) throws Exception;
}
public interface RoleDAO {public Set queryRoleNamesByUsername(String username) throws Exception;
}
public interface PermissionDAO {public Set queryPermissionsByUsername(String username) throws Exception;
}
org.apache.shiro shiro-spring 1.4.1
com.github.theborakompanioni thymeleaf-extras-shiro 2.0.0
@Configuration
public class ShiroConfig {@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}//自定义Realm@Beanpublic MyRealm getMyRealm(){MyRealm myRealm = new MyRealm();return myRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的filter.setSecurityManager(securityManager);Map filterMap = new HashMap<>();filterMap.put("/","anon");filterMap.put("/index.html","anon");filterMap.put("/login.html","anon");filterMap.put("/regist.html","anon");filterMap.put("/user/login","anon");filterMap.put("/user/regist","anon");filterMap.put("/layui/**","anon");filterMap.put("/**","authc");filter.setFilterChainDefinitionMap(filterMap);filter.setLoginUrl("/login.html");//设置未授权访问的页面路径()filter.setUnauthorizedUrl("/login.html");return filter;}}
/*** 1.创建一个类继承AuthorizingRealm类(实现了Realm接口的类)* 2.重写doGetAuthorizationInfo和doGetAuthenticationInfo方法* 3.重写getName方法返回当前realm的一个自定义名称*/
public class MyRealm extends AuthorizingRealm {@Resourceprivate UserDAO userDAO;@Resourceprivate RoleDAO roleDAO;@Resourceprivate PermissionDAO permissionDAO;public String getName() {return "myRealm";}/*** 获取授权数据(将当前用户的角色及权限信息查询出来)*/protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//获取用户的用户名String username = (String) principalCollection.iterator().next();//根据用户名查询当前用户的角色列表Set roleNames = roleDAO.queryRoleNamesByUsername(username);//根据用户名查询当前用户的权限列表Set ps = permissionDAO.queryPermissionsByUsername(username);SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setRoles(roleNames);info.setStringPermissions(ps);return info;}/*** 获取认证的安全数据(从数据库查询的用户的正确数据)*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//参数authenticationToken就是传递的 subject.login(token)// 从token中获取用户名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据用户名,从数据库查询当前用户的安全数据User user = userDAO.queryUserByUsername(username);AuthenticationInfo info = new SimpleAuthenticationInfo(username, //当前用户用户名user.getUserPwd(), //从数据库查询出来的安全密码getName());return info;}
}
- 明文-----(加密规则)-----密文
- 加密规则可以自定义,在项目开发中我们通常使用BASE64和MD5编码方式
- BASE64:可反编码的编码方式(对称)
- 明文----密文
- 密文----明文
- MD5: 不可逆的编码方式(非对称)
- 明文----密文
![]() |
配置matcher
@Configuration
public class ShiroConfig {//...@Beanpublic HashedCredentialsMatcher getHashedCredentialsMatcher(){HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();//matcher就是用来指定加密规则//加密方式matcher.setHashAlgorithmName("md5");//hash次数matcher.setHashIterations(1); //此处的循环次数要与用户注册是密码加密次数一致return matcher;}//自定义Realm@Beanpublic MyRealm getMyRealm( HashedCredentialsMatcher matcher ){MyRealm myRealm = new MyRealm();myRealm.setCredentialsMatcher(matcher);return myRealm;}//...
}
registh.html
UserController
@Controller
@RequestMapping("user")
public class UserController {@Resourceprivate UserServiceImpl userService;@RequestMapping("/regist")public String regist(String userName,String userPwd) {System.out.println("------注册");//注册的时候要对密码进行加密存储Md5Hash md5Hash = new Md5Hash(userPwd);System.out.println("--->>>"+ md5Hash.toHex());//加盐加密int num = new Random().nextInt(90000)+10000; //10000—99999String salt = num+"";Md5Hash md5Hash2 = new Md5Hash(userPwd,salt);System.out.println("--->>>"+md5Hash2);//加盐加密+多次hashMd5Hash md5Hash3 = new Md5Hash(userPwd,salt,3);System.out.println("--->>>"+md5Hash3);//SimpleHash hash = new SimpleHash("md5",userPwd,num,3);//将用户信息保存到数据库时,保存加密后的密码,如果生成的随机盐,盐也要保存return "login";}}
在自定义Realm中:
public class MyRealm extends AuthorizingRealm {@Resourceprivate UserDAO userDAO;@Resourceprivate RoleDAO roleDAO;@Resourceprivate PermissionDAO permissionDAO;public String getName() {return "myRealm";}/*** 获取认证的安全数据(从数据库查询的用户的正确数据)*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//参数authenticationToken就是传递的 subject.login(token)// 从token中获取用户名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据用户名,从数据库查询当前用户的安全数据User user = userDAO.queryUserByUsername(username);// AuthenticationInfo info = new SimpleAuthenticationInfo(
// username, //当前用户用户名
// user.getUserPwd(), //从数据库查询出来的安全密码
// getName());//如果数据库中用户的密码是加了盐的AuthenticationInfo info = new SimpleAuthenticationInfo(username, //当前用户用户名user.getUserPwd(), //从数据库查询出来的安全密码ByteSource.Util.bytes(user.getPwdSalt()),getName());return info;}
}
在Shiro过滤器中进行配置,配置logut对应的路径
filterMap.put("/exit","logout");
在页面的“退出”按钮上,跳转到logout对应的url
退出
用户登录成功之后,要进行响应的操作就需要有对应的权限;在进行操作之前对权限进行检查—授权
权限控制通常有两类做法:
- 不同身份的用户登录,我们现在不同的操作菜单(没有权限的菜单不现实)
- 对所有用户显示所有菜单,当用户点击菜单以后再验证当前用户是否有此权限,如果没有则提示权限不足
在菜单页面只显示当前用户拥有权限操作的菜单
shiro标签
- 入库
在shiro过滤器中对请求的url进行权限设置
filterMap.put("/c_add.html","perms[sys:c:save]");//设置未授权访问的页面路径—当权限不足时显示此页面
filter.setUnauthorizedUrl("/lesspermission.html");
配置Spring对Shiro注解的支持:ShiroConfig.java
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();autoProxyCreator.setProxyTargetClass(true);return autoProxyCreator;
}@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( DefaultWebSecurityManager securityManager){AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;
}
在请求的控制器添加权限注解
@Controller
@RequestMapping("customer")
public class CustomerController {@RequestMapping("list")//如果没有 sys:k:find 权限,则不允许执行此方法@RequiresPermissions("sys:k:find")// @RequiresRoles("")public String list(){System.out.println("----------->查询客户信息");return "customer_list";}}
通过全局异常处理,指定权限不足时的页面跳转
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandlerpublic String doException(Exception e){if(e instanceof AuthorizationException){return "lesspermission";}return null;}}
在代码中进行手动的权限校验
Subject subject = SecurityUtils.getSubject();
if(subject.isPermitted("sys:k:find")){System.out.println("----------->查询客户信息");return "customer_list";
}else{return "lesspermission";
}
使用Shiro进行权限管理过程中,每次授权都会访问realm中的doGetAuthorizationInfo方法查询当前用户的角色及权限信息,如果系统的用户量比较大则会对数据库造成比较大的压力
Shiro支持缓存以降低对数据库的访问压力(缓存的是授权信息)
org.springframework.boot spring-boot-starter-cache
net.sf.ehcache ehcache
org.apache.shiro shiro-ehcache 1.4.0
@Bean
public EhCacheManager getEhCacheManager(){EhCacheManager ehCacheManager = new EhCacheManager();ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");return ehCacheManager;
}@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(getEhCacheManager());return securityManager;
}
Shiro进行认证和授权是基于session实现的,Shiro包含了对session的管理
如果我们需要对session进行管理
配置自定义SessionManager:ShiroConfig.java
@Bean
public DefaultWebSessionManager getDefaultWebSessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();System.out.println("----------"+sessionManager.getGlobalSessionTimeout()); // 1800000//配置sessionManagersessionManager.setGlobalSessionTimeout(5*60*1000);return sessionManager;
}@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(getEhCacheManager());securityManager.setSessionManager(getDefaultWebSessionManager());return securityManager;
}
解疑
如何避免登录页面显示在页面框架中?
在登录页面添如下JS代码:
将用户对页面访问的权限分为三个级别:
- 未认证—可访问的页面—(陌生人)—问候
- login.html、regist.html
- 记住我—可访问的页面—(前女友)—朋友间的拥抱
- info.html
- 已认证—可访问的页面—(现女友)—牵手
- 转账.html
// anon 表示未认证可访问的url
// user 表示记住我可访问的url(已认证也可以访问)
//authc 表示已认证可访问的url
//perms 表示必须具备指定的权限才可访问
//logout 表示指定退出的url
filterMap.put("/","anon");
filterMap.put("/index.html","user");
filterMap.put("/login.html","anon");
filterMap.put("/regist.html","anon");
filterMap.put("/user/login","anon");
filterMap.put("/user/regist","anon");
filterMap.put("/layui/**","anon");
filterMap.put("/**","authc");
filterMap.put("/c_add.html","perms[sys:c:save]");
filterMap.put("/exit","logout");
@Bean
public CookieRememberMeManager cookieRememberMeManager(){CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();//cookie必须设置nameSimpleCookie cookie = new SimpleCookie("rememberMe");cookie.setMaxAge(30*24*60*60);rememberMeManager.setCookie(cookie);return rememberMeManager;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(getEhCacheManager());securityManager.setSessionManager(getDefaultWebSessionManager());//设置remember管理器securityManager.setRememberMeManager(cookieRememberMeManager());return securityManager;
}
@Controller
@RequestMapping("user")
public class UserController {@Resourceprivate UserServiceImpl userService;@RequestMapping("login")public String login(String userName,String userPwd,boolean rememberMe){try {userService.checkLogin(userName,userPwd,rememberMe);System.out.println("------登录成功!");return "index";} catch (Exception e) {System.out.println("------登录失败!");return "login";}}//...
}
@Service
public class UserServiceImpl {public void checkLogin(String userName, String userPwd,boolean rememberMe) throws Exception {//Shiro进行认证 ——入口Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);token.setRememberMe(rememberMe);subject.login(token);}
}
定义多个Realm
UserRealm
public class UserRealm extends AuthorizingRealm {Logger logger = LoggerFactory.getLogger(UserRealm.class);@Overridepublic String getName() {return "UserRealm";}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {logger.info("--------------------------------UserRealm");//从token中获取usernameUsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据username从users表中查询用户信息SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"123456",getName());return info;}
}
ManagerRealm
public class ManagerRealm extends AuthorizingRealm {Logger logger = LoggerFactory.getLogger(ManagerRealm.class);@Overridepublic String getName() {return "ManagerRealm";}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {logger.info("--------------------------------ManagerRealm");//从token中获取usernameUsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据username从吗managers表中查询用户信息SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"222222",getName());return info;}
}
在ShiroConfig.java中为SecurityManager配置多个Realm
@Configuration
public class ShiroConfig {@Beanpublic UserRealm userRealm(){UserRealm userRealm = new UserRealm();return userRealm;}@Beanpublic ManagerRealm managerRealm(){ManagerRealm managerRealm = new ManagerRealm();return managerRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//securityManager中配置多个realmCollection realms = new ArrayList<>();realms.add(userRealm());realms.add(managerRealm());securityManager.setRealms(realms);return securityManager;}//...}
测试代码:
login.html
UserController.java
@Controller
@RequestMapping("user")
public class UserController {Logger logger = LoggerFactory.getLogger(UserController.class);@RequestMapping("login")public String login(String userName,String userPwd, String loginType){logger.info("~~~~~~~~~~~~~UserController-login");try{UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);Subject subject = SecurityUtils.getSubject();subject.login(token);return "index";}catch (Exception e){return "login";}}}
![]() |
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {this.assertRealmsConfigured();Collection realms = this.getRealms();// this.doMultiRealmAuthentication(realms, authenticationToken);中的realms参数就是认证会执行的Realmreturn realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);}
根据不同的条件执行不同的Realm
流程分析
实现案例:用户不同身份登录执行不同的Realm
自定义Realm(UserRealm\ManagerRealm)
Realm的声明及配置
自定义Token
public class MyToken extends UsernamePasswordToken {private String loginType;public MyToken(String userName,String userPwd, String loginType) {super(userName,userPwd);this.loginType = loginType;}public String getLoginType() {return loginType;}public void setLoginType(String loginType) {this.loginType = loginType;}
}
自定义认证器
public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {Logger logger = LoggerFactory.getLogger(MyModularRealmAuthenticator.class);@Overrideprotected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {logger.info("------------------------------MyModularRealmAuthenticator");this.assertRealmsConfigured();Collection realms = this.getRealms();MyToken token = (MyToken) authenticationToken;String loginType = token.getLoginType(); // Userlogger.info("------------------------------loginType:"+loginType);Collection typeRealms = new ArrayList<>();for(Realm realm:realms){if(realm.getName().startsWith(loginType)){ //UserRealmtypeRealms.add(realm);}}if(typeRealms.size()==1){return this.doSingleRealmAuthentication((Realm)typeRealms.iterator().next(), authenticationToken);}else{return this.doMultiRealmAuthentication(typeRealms, authenticationToken);}}}
配置自定义认证器
@Configuration
public class ShiroConfig {@Beanpublic UserRealm userRealm(){UserRealm userRealm = new UserRealm();return userRealm;}@Beanpublic ManagerRealm managerRealm(){ManagerRealm managerRealm = new ManagerRealm();return managerRealm;}@Beanpublic MyModularRealmAuthenticator myModularRealmAuthenticator(){MyModularRealmAuthenticator myModularRealmAuthenticator = new MyModularRealmAuthenticator();return myModularRealmAuthenticator;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//配置自定义认证器(放在realms设置之前)securityManager.setAuthenticator(myModularRealmAuthenticator());//securityManager中配置多个realmCollection realms = new ArrayList<>();realms.add(userRealm());realms.add(managerRealm());securityManager.setRealms(realms);return securityManager;}//...}
测试:控制器接受数据进行认证
@Controller
@RequestMapping("user")
public class UserController {Logger logger = LoggerFactory.getLogger(UserController.class);@RequestMapping("login")public String login(String userName,String userPwd, String loginType){logger.info("~~~~~~~~~~~~~UserController-login");try{//UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);MyToken token = new MyToken(userName,userPwd,loginType);Subject subject = SecurityUtils.getSubject();subject.login(token);return "index";}catch (Exception e){return "login";}}}
View JSP(Java Server Page)
Control Servlet
Model JDBC(Java Database Connection)
第一阶段的项目:JSP/Servlet+JDBC
单体项目:项目的前端页面与服务端的代码在同一个项目中(部署在同一个服务器上)
![]() |