不同身份的用户进入到系统所能够完成的操作是不相同的,我们对不同用户进行的可执行的操作的管理称之为权限管理。
权限管理设计
基于主页的权限管理(不同用户使用不同的主页,权限通过主页功能菜单进行限制)
基于用户和权限的权限管理
基于角色的访问控制
认证:对用户的身份进行检查(登录验证)
授权:对用户的权限进行检查(是否有对应的操作权限)
流程示意图:
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
,缓存管理器<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>1.4.1version>
dependency>
[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.创建realm
IniRealm 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.导入依赖
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro‐coreartifactId>
<version>1.4.1version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro‐webartifactId>
<version>1.4.1version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro‐springartifactId>
<version>1.4.1version>
dependency>
2.在web.xml中添加shrio对应的过滤器配置
拦截所有请求
<web‐app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web‐app_3_1.xsd"
version="3.1">
<context‐param>
<param‐name>contextConfigLocationparam‐name>
<param‐value>classpath:spring‐*.xmlparam‐value>
context‐param>
<listener>
<listener‐class>org.springframework.web.context.ContextLoaderListenerlistener‐class>
listener>
<servlet>
<servlet‐name>springMVCservlet‐name>
<servlet‐class>org.springframework.web.servlet.DispatcherServletservlet‐class>
<init‐param>
<param‐name>contextConfigLocationparam‐name>
<param‐value>classpath:spring‐context*.xmlparam‐value>
init‐param>
servlet>
<servlet‐mapping>
<servlet‐name>springMVCservlet‐name>
<url‐pattern>*.dourl‐pattern>
servlet‐mapping>
<filter>
<filter‐name>shiroFilterfilter‐name>
<filter‐class>org.springframework.web.filter.DelegatingFilterProxyfilter‐class>
<init‐param>
<param‐name>targetFilterLifecycleparam‐name>
<param‐value>trueparam‐value>
init‐param>
filter>
<filter‐mapping>
<filter‐name>shiroFilterfilter‐name>
<url‐pattern>/*url‐pattern>
filter‐mapping>
web‐app>
3.在spring配置文件中,配置shrio框架所需的内容
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring‐beans.xsd">
<!‐‐shiro过滤器‐‐>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager">property>
<property name="loginUrl" value="/login.jsp">property>
<!‐‐配置登录成功之后跳转的页面路径,此配置一般不使用,一般在LoginController中处理
逻辑
name="successUrl" value="/index.jsp"> property>
‐‐>
<property name="unauthorizedUrl" value="/">property>
<property name="filterChainDefinitions">
<value>/**=anonvalue>
property>
bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor">bean>
<!‐‐证书匹配器‐‐>
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.Md5CredentialsMatcher">bean>
<!‐‐使用自带的Realm‐‐>
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="credentialsMatcher" ref="credentialsMatcher">property>
<property name="permissionsLookupEnabled" value="true">property>
<property name="dataSource" ref="dataSource">property>
bean>
<!‐‐缓存管理‐‐>
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager">bean>
<!‐‐shiro安全管理器‐‐>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="jdbcRealm">property>
<property name="cacheManager" ref="cacheManager">property>
bean>
beans>
4.配置权限验证使用注解
<!‐‐配置权限验证使用注解‐‐>
<aop:config proxy‐target‐class="true">aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager">property>
bean>
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 {
@Autowired
private 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
依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.0version>
dependency>
配置
spring:
datasource:
druid:
url: jdbc:mysql://47.96.11.185:3306/test
# MySQL如果是8.x com.mysql.cj.jdbc.Driver
driver-class-name: com.mysql.jdbc.Driver
username: root
password: admin123
initial-size: 1
min-idle: 1
max-active: 20
mybatis:
mapper-locations: classpath:mappers/*Mapper.xml
type-aliases-package: com.qfedu.springbootssm.beans
导入依赖
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.1version>
dependency>
Shiro配置(java配置方式)
@Configuration
public class ShiroConfig {
@Bean
public IniRealm getIniRealm(){
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
return iniRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(IniRealm iniRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//securityManager要完成校验,需要realm
securityManager.setRealm(iniRealm);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的
filter.setSecurityManager(securityManager);
//设置shiro的拦截规则
// anon 匿名用户可访问
// authc 认证用户可访问
// user 使用RemeberMe的用户可访问
// perms 对应权限可访问
// role 对应的角色可访问
Map<String,String> 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 {
@Resource
private 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
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
login
<hr/>
<form action="user/login">
<p>帐号:<input type="text" name="userName"/>p>
<p>密码:<input type="text" name="userPwd"/>p>
<p><input type="submit" value="登录"/>p>
form>
body>
html>
用户信息表: 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
添加依赖
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.1version>
dependency>
配置Shiro
@Configuration
public class ShiroConfig {
@Bean
public JdbcRealm getJdbcRealm(DataSource dataSource){
JdbcRealm jdbcRealm = new JdbcRealm();
//JdbcRealm会自行从数据库查询用户及权限数据(数据库的表结构要符合JdbcRealm的规范)
jdbcRealm.setDataSource(dataSource);
//JdbcRealm默认开启认证功能,需要手动开启授权功能
jdbcRealm.setPermissionsLookupEnabled(true);
return jdbcRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(JdbcRealm jdbcRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(jdbcRealm);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的
filter.setSecurityManager(securityManager);
Map<String,String> 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模版中引用:
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.0.0version>
dependency>
@Configuration
public class ShiroConfig {
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
//...
}
<html xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
...
html>
常用标签
guest,判断用户是否是游客身份,如果是游客身份则显示此标签内容
<shiro:guest>
欢迎游客访问,<a href="login.html">登录a>
shiro:guest>
user,判断用户是否是认证身份,如果是认证身份则显示此标签内容
principal,获取当前登录用户名
<shiro:user>
用户[<shiro:principal/>]欢迎您!
shiro:user>
notAuthenticated/authenticated
hasRole
hasPermission
DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
index
<hr/>
<shiro:guest>
欢迎游客访问,<a href="login.html">登录a>
shiro:guest>
<shiro:user>
用户[<shiro:principal/>]欢迎您!
当前用户为<shiro:hasRole name="admin">超级管理员shiro:hasRole>
<shiro:hasRole name="cmanager">仓管人员shiro:hasRole>
<shiro:hasRole name="xmanager">销售人员shiro:hasRole>
<shiro:hasRole name="kmanager">客服人员shiro:hasRole>
<shiro:hasRole name="zmanager">行政人员shiro:hasRole>
shiro:user>
<hr/>
仓库管理
<ul>
<shiro:hasPermission name="sys:c:save"><li><a href="#">入库a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:c:delete"><li><a href="#">出库a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:c:update"><li><a href="#">修改a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:c:find"><li><a href="#">查询a>li>shiro:hasPermission>
ul>
订单管理
<ul>
<shiro:hasPermission name="sys:x:save"><li><a href="#">添加订单a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:x:delete"><li><a href="#">删除订单a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:x:update"><li><a href="#">修改订单a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:x:find"><li><a href="#">查询订单a>li>shiro:hasPermission>
ul>
客户管理
<ul>
<shiro:hasPermission name="sys:k:save"><li><a href="#">添加客户a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:k:delete"><li><a href="#">删除客户a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:k:update"><li><a href="#">修改客户a>li>shiro:hasPermission>
<shiro:hasPermission name="sys:k:find"><li><a href="#">查询客户a>li>shiro:hasPermission>
ul>
body>
html>
使用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, -- 1
permission_code varchar(60) not null, -- sys:c:find
permission_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;
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qfedu.shiro4.dao.UserDAO">
<resultMap id="userMap" type="User">
<id column="user_id" property="userId">id>
<result column="username" property="userName"/>
<result column="password" property="userPwd"/>
<result column="password_salt" property="pwdSalt"/>
resultMap>
<select id="queryUserByUsername" resultMap="userMap">
select * from tb_users
where username=#{username}
select>
mapper>
public interface RoleDAO {
public Set queryRoleNamesByUsername(String username) throws Exception;
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qfedu.shiro4.dao.RoleDAO">
<select id="queryRoleNamesByUsername" resultSets="java.util.Set" resultType="string">
select role_name
from tb_users inner join tb_urs
on tb_users.user_id = tb_urs.uid
inner join tb_roles
on tb_urs.rid = tb_roles.role_id
where tb_users.username=#{username}
select>
mapper>
public interface PermissionDAO {
public Set<String> queryPermissionsByUsername(String username) throws Exception;
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qfedu.shiro4.dao.PermissionDAO">
<select id="queryPermissionsByUsername" resultSets="java.util.Set" resultType="string">
select tb_permissions.permission_code from tb_users
inner join tb_urs on tb_users.user_id=tb_urs.uid
inner join tb_roles on tb_urs.rid=tb_roles.role_id
inner join tb_rps on tb_roles.role_id=tb_rps.rid
inner join tb_permissions on tb_rps.pid=tb_permissions.permission_id
where tb_users.username=#{username}
select>
mapper>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.1version>
dependency>
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.0.0version>
dependency>
@Configuration
public class ShiroConfig {
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
//自定义Realm
@Bean
public MyRealm getMyRealm(){
MyRealm myRealm = new MyRealm();
return myRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的
filter.setSecurityManager(securityManager);
Map<String,String> 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 {
@Resource
private UserDAO userDAO;
@Resource
private RoleDAO roleDAO;
@Resource
private PermissionDAO permissionDAO;
public String getName() {
return "myRealm";
}
/**
* 获取授权数据(将当前用户的角色及权限信息查询出来)
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取用户的用户名
String username = (String) principalCollection.iterator().next();
//根据用户名查询当前用户的角色列表
Set<String> roleNames = roleDAO.queryRoleNamesByUsername(username);
//根据用户名查询当前用户的权限列表
Set<String> 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 {
//...
@Bean
public HashedCredentialsMatcher getHashedCredentialsMatcher(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//matcher就是用来指定加密规则
//加密方式
matcher.setHashAlgorithmName("md5");
//hash次数
matcher.setHashIterations(1); //此处的循环次数要与用户注册是密码加密次数一致
return matcher;
}
//自定义Realm
@Bean
public MyRealm getMyRealm( HashedCredentialsMatcher matcher ){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(matcher);
return myRealm;
}
//...
}
registh.html
<form action="/user/regist" method="post">
<p>帐号:<input type="text" name="userName"/>p>
<p>密码:<input type="text" name="userPwd"/>p>
<p><input type="submit" value="提交注册"/>p>
form>
UserController
@Controller
@RequestMapping("user")
public class UserController {
@Resource
private 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—99999
String salt = num+"";
Md5Hash md5Hash2 = new Md5Hash(userPwd,salt);
System.out.println("--->>>"+md5Hash2);
//加盐加密+多次hash
Md5Hash 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 {
@Resource
private UserDAO userDAO;
@Resource
private RoleDAO roleDAO;
@Resource
private 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
<a href="exit">退出a>
用户登录成功之后,要进行响应的操作就需要有对应的权限;在进行操作之前对权限进行检查—授权
权限控制通常有两类做法:
- 不同身份的用户登录,我们现在不同的操作菜单(没有权限的菜单不现实)
- 对所有用户显示所有菜单,当用户点击菜单以后再验证当前用户是否有此权限,如果没有则提示权限不足
在菜单页面只显示当前用户拥有权限操作的菜单
shiro标签
<shiro:hasPermission name="sys:c:save">
<dd><a href="javascript:;">入库a>dd>
shiro:hasPermission>
在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 {
@ExceptionHandler
public 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支持缓存以降低对数据库的访问压力(缓存的是授权信息)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-ehcacheartifactId>
<version>1.4.0version>
dependency>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="C:\TEMP" />
<cache name="users" timeToLiveSeconds="300" maxEntriesLocalHeap="1000"/>
<defaultCache name="defaultCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
ehcache>
@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
//配置sessionManager
sessionManager.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代码:
<script type="text/javascript">
//页面框架
if(window.top != window.self){
window.top.location = window.self.location;
}
script>
将用户对页面访问的权限分为三个级别:
- 未认证—可访问的页面—(陌生人)—问候
- 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必须设置name
SimpleCookie 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;
}
<form action="/user/login" method="post">
<p>帐号:<input type="text" name="userName"/>p>
<p>密码:<input type="text" name="userPwd"/>p>
<p>记住我:<input type="checkbox" name="rememberMe"/>p>
<p><input type="submit" value="登录"/>p>
form>
@Controller
@RequestMapping("user")
public class UserController {
@Resource
private 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);
@Override
public String getName() {
return "UserRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("--------------------------------UserRealm");
//从token中获取username
UsernamePasswordToken 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);
@Override
public String getName() {
return "ManagerRealm";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("--------------------------------ManagerRealm");
//从token中获取username
UsernamePasswordToken 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 {
@Bean
public UserRealm userRealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
@Bean
public ManagerRealm managerRealm(){
ManagerRealm managerRealm = new ManagerRealm();
return managerRealm;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//securityManager中配置多个realm
Collection<Realm> realms = new ArrayList<>();
realms.add(userRealm());
realms.add(managerRealm());
securityManager.setRealms(realms);
return securityManager;
}
//...
}
测试代码:
login.html
<form action="user/login" method="post">
<p>帐号:<input type="text" name="userName"/>p>
<p>密码:<input type="text" name="userPwd"/>p>
<p><input type="radio" name="loginType" value="User"/>普通用户
<input type="radio" name="loginType" value="Manager"/>管理员p>
<p><input type="submit" value="登录"/>p>
form>
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<Realm> realms = this.getRealms();
// this.doMultiRealmAuthentication(realms, authenticationToken);中的realms参数就是认证会执行的Realm
return 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);
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info("------------------------------MyModularRealmAuthenticator");
this.assertRealmsConfigured();
Collection<Realm> realms = this.getRealms();
MyToken token = (MyToken) authenticationToken;
String loginType = token.getLoginType(); // User
logger.info("------------------------------loginType:"+loginType);
Collection<Realm> typeRealms = new ArrayList<>();
for(Realm realm:realms){
if(realm.getName().startsWith(loginType)){ //UserRealm
typeRealms.add(realm);
}
}
if(typeRealms.size()==1){
return this.doSingleRealmAuthentication((Realm)typeRealms.iterator().next(), authenticationToken);
}else{
return this.doMultiRealmAuthentication(typeRealms, authenticationToken);
}
}
}
配置自定义认证器
@Configuration
public class ShiroConfig {
@Bean
public UserRealm userRealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
@Bean
public ManagerRealm managerRealm(){
ManagerRealm managerRealm = new ManagerRealm();
return managerRealm;
}
@Bean
public MyModularRealmAuthenticator myModularRealmAuthenticator(){
MyModularRealmAuthenticator myModularRealmAuthenticator = new MyModularRealmAuthenticator();
return myModularRealmAuthenticator;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//配置自定义认证器(放在realms设置之前)
securityManager.setAuthenticator(myModularRealmAuthenticator());
//securityManager中配置多个realm
Collection<Realm> realms = new ArrayList<>();
realms.add(userRealm());
realms.add(managerRealm());
securityManager.setRealms(realms);
return securityManager;
}
//...
}
测试:控制器接受数据进行认证
<form action="user/login" method="post">
<p>帐号:<input type="text" name="userName"/>p>
<p>密码:<input type="text" name="userPwd"/>p>
<p><input type="radio" name="loginType" value="User" checked/>普通用户
<input type="radio" name="loginType" value="Manager"/>管理员p>
<p><input type="submit" value="登录"/>p>
form>
@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
单体项目:项目的前端页面与服务端的代码在同一个项目中(部署在同一个服务器上)