目录
原理/流程
原理/流程
1.核心组件
1》subject 封装了用户名密码,用户权限,用户角色等信息
2》securitymanager负责进行校验用户名密码,权限等操作
3》realm,相当于securitymanager的数据源,去数据库查询,将权限,是否登录成功等信息提供给secutirymanager,
thymeleaf使用shiro标签
1 必须导入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2 在shiroconfig.java文件中配置方言支持(就是将shrio的语法转为tymeleaf的语法)
//配置方言
@Bean
public ShiroDialect getshirodialect() {
return new ShiroDialect();
}
3 在html中添加依赖
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
1 设置不同的登录状态显示不同的标签
需要shiro的拦截器将该页面设置为未登录也可以访问,或者默认就是未登录可访问
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>登录成功</h1>
<shiro:guest>
<div>欢迎游客访问,</div><a href="/">登录</a>
</shiro:guest>
<shiro:user>
已经登录的用户!!
</shiro:user>
</body>
</html>
2.获取当前登录的用户名
<shiro:principal/>
3/如果用户有某个角色就显示对应的字
<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>
4/用户的角色有某个权限就显示对应的字
<ul>
<shiro:hasPermission name="sys:x:find"><li><a href="#">查找销售记录</a> </li></shiro:hasPermission>
</ul>
总结:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>登录成功</h1>
<shiro:guest>
<div>欢迎游客访问,</div><a href="/">登录</a>
</shiro:guest>
<shiro:user>
已经登录的用户!!
用户名是 <shiro:principal/>
<div>职位是:
<shiro:hasRole name="admin">超级管理员</shiro:hasRole>
<shiro:hasRole name="cmanager">仓库管理</shiro:hasRole>
<shiro:hasRole name="seller">销售人员</shiro:hasRole>
<shiro:hasRole name="kmanager">客服人员</shiro:hasRole>
<shiro:hasRole name="zmanager">行政人员</shiro:hasRole>
</div>
<div>
具有的权限如下:
<shiro:hasPermission name="order-add"><li><a href="#">添加订单</a> </li></shiro:hasPermission>
<shiro:hasPermission name="order-del"><li><a href="#">删除订单</a> </li></shiro:hasPermission>
<shiro:hasPermission name="order-list"><li><a href="#">查询订单</a> </li></shiro:hasPermission>
<shiro:hasPermission name="user_query"><li><a href="#">张三不具有这个权限,所以应该不显示</a> </li></shiro:hasPermission>
</div>
</shiro:user>
</body>
</html>
jdbcrealm
1.在数据库中创建表,表名必须是users user_roles roles_permissions
字段名分别是
DROP TABLE
IF EXISTS `users`;
CREATE TABLE users (
id INT PRIMARY KEY auto_increment,
username VARCHAR (60) NOT NULL,
PASSWORD VARCHAR (20) NOT NULL,
password_salt VARCHAR (20)
);
INSERT INTO users (username, PASSWORD)
VALUES
('zhangsan', '123');
INSERT INTO users (username, PASSWORD)
VALUES
('lisi', '456');
INSERT INTO users (username, PASSWORD)
VALUES
('wangwu', '789');
DROP TABLE
IF EXISTS `user_roles`;
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', 'manager');
INSERT INTO user_roles (username, role_name)
VALUES
('wangwu', 'saler');
DROP TABLE
IF EXISTS `roles_permissions`;
CREATE TABLE roles_permissions (
id INT PRIMARY KEY auto_increment,
rolename VARCHAR (60) NOT NULL,
permission VARCHAR (100) NOT NULL
);
INSERT INTO roles_permissions (rolename, permission)
VALUES
('admin', '*');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('manager', 'sys:m:update');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('manager', 'sys:m:select');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('manager', 'sys:m:delete');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('manager', 'sys:m:add');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('saler', 'sys:s:update');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('saler', 'sys:s:delete');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('saler', 'sys:s:add');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('saler', 'sys:s:select');
INSERT INTO roles_permissions (rolename, permission)
VALUES
('saler', 'sys:k:manager');
1.在application.yaml中配置数据库的数据源信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/my_db_01
username: root
password: mysql
2.在shiroconfig中配置 jdbcrealm,并将其作为realm绑定到SecurityManager,在查询时,shiro会自己链接数据源,查询登录和角色信息(前提是数据库的字段 表名要和规定的一致,看上面)
@Autowired
DataSource dataSource;
@Bean
public JdbcRealm getjdbcRealm(){
JdbcRealm jdbcRealm=new JdbcRealm();
// 只需要给数据源,回自动查找数据库
jdbcRealm.setDataSource(dataSource);
// 默认只开启认证功能,手动开启授权功能
jdbcRealm.setPermissionsLookupEnabled(true);
return jdbcRealm;
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager();
SecurityManager.setRealm(getjdbcRealm());
return SecurityManager;
}
调用不变,更换realm只是后面数据的来源变了,前面验证该怎么用还是怎么用
同样的,登录成功不报错,不成功报错
try {
subject.login(usernamePasswordToken);
return "success";
}
catch (Exception e)
{
System.out.println(e);
System.out.println("拦截到了错误的账号或者密码");
return "error1";
}
自定义realm
自定义的realm可以读取数据库中的加密,加盐了的数据。
realm就是数据源,负责从数据库中查询数据,不应该由我们调用,我们写好之后,配置给securitymanager,由它自动调用
- 自定义realm类要继承AuthorizingRealm
- 重写doGetAuthorizationInfo查询用户的角色和权限 doGetAuthenticationInfo获取用户信息
- 重写getname()方法(因为可能自定义多个realm,要区分)
doGetAuthenticationInfo方法要:获取数据源,链接数据库,从用户表中查询用户名对应的信息,能查到就返回用户信息,查不到就返回null(并不进行登录验证,只是将该用户名对应的用户信息查询出来)
//获取认证信息
@Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String uname = usernamePasswordToken.getUsername();
User user = userMapper.checklogin(uname);
System.out.println(user);
if (user == null) {
return null;
} else {
AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getname());
return info;
}
}
doGetAuthorizationInfo查询用户具有的权限 和角色信息
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取当前的用户名
String username = (String) principalCollection.iterator().next();
// 查询权限信息
Set<String> permissions =powerMapper.selectpermissions(username);
System.out.println("permissions"+permissions);
// 查询角色信息
Set<String> roles = powerMapper.selectrole(username);
System.out.println("roles如下:"+roles);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 设置角色信息
info.setRoles(roles);
// 设置权限信息
info.setStringPermissions(permissions);
return info;
}
配置好后的myrealm
package com.example.demo79ini.Config;
import com.example.demo79ini.mapper.PowerMapper;
import com.example.demo79ini.mapper.UserMapper;
import com.example.demo79ini.pojo.User;
import com.sun.org.apache.bcel.internal.generic.RETURN;
import org.apache.shiro.authc.*;
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.util.ByteSource;
import org.apache.tomcat.util.net.openssl.ciphers.Authentication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.security.sasl.AuthorizeCallback;
import javax.sound.sampled.Line;
import java.util.Set;
@Configuration
public class MyRealm extends AuthorizingRealm {
@Autowired
UserMapper userMapper;
@Autowired
PowerMapper powerMapper;
public String getname() {
return "MyRealm";
}
// // 获取授权信息 将当前用户的权限和角色信息查询出来,这里的已经登录了用户,所以直接获取权限,角色即可
// @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取当前的用户名
String username = (String) principalCollection.iterator().next();
// 查询权限信息
Set<String> permissions =powerMapper.selectpermissions(username);
System.out.println("permissions"+permissions);
// 查询角色信息
Set<String> roles = powerMapper.selectrole(username);
System.out.println("roles如下:"+roles);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 设置角色信息
info.setRoles(roles);
// 设置权限信息
info.setStringPermissions(permissions);
return info;
}
//获取认证信息
@Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String uname = usernamePasswordToken.getUsername();
User user = userMapper.checklogin(uname);
System.out.println(user+"从数据库中查出来的user");
if (user == null) {
return null;
} else {
AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getPassword_salt()), getname());
return info;
}
}
}
可以查询出用户对应的角色信息
查询出对应的权限信息
数据库的查询语句如下
<?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.example.demo79ini.mapper.PowerMapper">
<select id="selectrole" resultType="java.lang.String" parameterType="string">
select role_name
from tb_users
INNER JOIN tb_roles_users on tb_users.id = tb_roles_users.uid
INNER JOIN tb_roles on tb_roles_users.rid = tb_roles.role_id
where tb_users.username = #{username}
</select>
<select id="selectpermissions" resultType="java.lang.String">
select permission_code
from tb_users
INNER JOIN tb_roles_users on tb_users.id = tb_roles_users.uid
INNER JOIN tb_roles on tb_roles_users.rid = tb_roles.role_id
INNER JOIN tb_permissions_roles on tb_permissions_roles.role_id = tb_roles.role_id
INNER JOIN tb_permissions on tb_permissions.permission_id = tb_permissions_roles.permission_id
where tb_users.username = #{username}
</select>
</mapper>
设置好之后需要在securitymanager中进行绑定
@Autowired
MyRealm myRealm;
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
对密码进行加密
MD5算法,是不能解密的,但是如果明文一样,加密次数一样,则生成的md5密文也是一样的 所以要在检查用户密码前,将用户的密码进行相同次数的MD5加密,如果和数据库中的一致,则是对的用户名和密码。
查询加密后的密码
1.设置 HashedCredentialsMatcher类 在类中设置要使用的加密算法和加密算法要执行的次数
@Bean
public HashedCredentialsMatcher gethHashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 指定加密的算法
matcher.setHashAlgorithmName("md5");
// 设置加密算法的执行次数
matcher.setHashIterations(1);
return matcher;
}
2.在myrealm中绑定 HashedCredentialsMatcher类
@Bean
public MyRealm MyRealm1() {
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(gethHashedCredentialsMatcher());
return myRealm;
}
3.注意securitymanager类中,一定要使用刚才设置了setCredentialsMatcher的 realm
4.配置好加密类之后,在用户登录时,会自动给密码加密指定的次数,后再匹配数据库中的数据
@RequestMapping("/check_login")
public String check_loging(String uname,String upwd)
{
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(uname,upwd);
Subject subject= SecurityUtils.getSubject();
// System.out.println(subject+"contoller调用时的subject");
try {
subject.login(usernamePasswordToken);
return "success";
}
catch (Exception e)
{
// System.out.println(e);
System.out.println("拦截到了错误的账号或者密码");
return "error1";
}
}
向数据库中添加加密后的数据
@RequestMapping("/register")
public String register(String uname,String upwd){
System.out.println(uname+upwd+"到达注册页面的用户名和密码");
Md5Hash md5Hash=new Md5Hash(upwd);
System.out.println("一次加密"+md5Hash+"");
userMapper.adduser(new User( uname,md5Hash+"",null));
return "login";
}
加盐
在加密的基础上,可以让密码更安全。
就是在加密前,给密码添加一个随机的前缀或者是后缀,之后再进行加密
一般盐随机生成,一个密码对应一个盐,所以盐和密码要一同存储在数据库中
注意一定要在加密的基础上,即该realm在shiroconfig中配置了加密算法,
@Bean
public HashedCredentialsMatcher gethHashedCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 指定加密的算法
matcher.setHashAlgorithmName("md5");
// 设置加密算法的执行次数
matcher.setHashIterations(1);
return matcher;
}
@Bean
public ManagerRealm managerRealm()
{
ManagerRealm managerRealm=new ManagerRealm();
managerRealm.setCredentialsMatcher(gethHashedCredentialsMatcher());
return managerRealm;
}
查询加盐后的数据
在myrealm中,查询数据库,会把密码和对应的盐查出来,在 AuthenticationInfo 中,需要将密码和对应的 盐作为参数,返回给sercurityManger。
@Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
String uname = usernamePasswordToken.getUsername();
User user = userMapper.checklogin(uname);
System.out.println(user+"从数据库中查出来的user");
if (user == null) {
return null;
} else {
//只有这一句
AuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getPassword_salt()), getname());
return info;
}
}
给密码加盐加密后填入数据库
@RequestMapping("/register")
public String register(String uname,String upwd){
System.out.println(uname+upwd+"到达注册页面的用户名和密码");
int num=new Random().nextInt(90000)+10000;//随机生成字符串
Md5Hash md5Hash=new Md5Hash(upwd,num+"");
String pwd_md5=md5Hash+"";
System.out.println("一次加盐加密"+pwd_md5+"盐是"+num);
userMapper.adduser(new User( uname,pwd_md5,num+""));
return "login";
}
用户授权
设置用户具有某些权限才可以访问某些页面
方式一
在ShiroConfig类中设置
@Bean
public ShiroFilterFactoryBean shiroFilter() {
// anon匿名用户可以访问
// authc认证用户可以访问
// user 使用了rememerme的可以访问
// perms对应的权限可以访问
// 对应的角色可以访问
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(getdefaultWebSecurityManager());
Map<String, String> filterMap = new HashMap<>();
// 不设置请求的权限,就是anon,不登录也可以访问
filterMap.put("/login", "anon");
filterMap.put("/", "anon");
// filterMap.put("/success", "authc");
filter.setFilterChainDefinitionMap(filterMap);
// 下面的路径是需要springmvc进行解析的 不能直接跳转到该页面
// @RequestMapping("/noquanxian")
// 设置默认的登录页面,这个好像不起作用,springmvc拦截/请求即可
filter.setLoginUrl("/noquanxian");
// 设置未授权,但是访问了该页面就跳转到
filter.setUnauthorizedUrl("/noquanxian");
return filter;
}
方式二
步骤:
1...配置spring对shiro注解的支持,在shiroconfig中 添加两个类,没必要理解,记住就好
//配置注解管理权限 ,记住就好
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(getdefaultWebSecurityManager());
return authorizationAttributeSourceAdvisor;
}
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoproxycreator = new DefaultAdvisorAutoProxyCreator();
autoproxycreator.setProxyTargetClass(true);
return autoproxycreator;
}
2...在controller中添加@RequiresPermissions注解
@RequestMapping("c_delete")
@RequiresPermissions("sys:c:delete")
public String c_delete(){
System.out.println("删除客户信息");
return "c_delete";
}
用户访问要访问的页面,如果具有sys:c:delete权限,则可以正常访问,如果不具有,会报500错误。
使用此种方式拦截,如果访问了未经授权的页面,会报如下错误
方式三
手动验证 不同于前两种,必须要拦截一个路径,这种方法不需要拦截路径,所以可以用在service层,用户必须具有某个权限才可以进行操作
@Service
public class UserService {
public String c_update(Subject subject){
if(subject.isPermitted("sys:c:update"))
{
System.out.println("更新客户信息");
return "c_update";
}
else
{
System.out.println("权限不足,不能更新");
return "error1";
}
}
}
缓存
发现登录查询只执行一次,但是权限查询,有多少个查询标签,就执行多少次该方法
为了解决这种情况,需要使用缓存
1添加依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2配置缓存策略
在resouce目录下,创建一个xml文件,名字随便起,一般叫ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
dynamicConfig="false"
updateCheck="false">
<!--如果内存满了,会存储在这个内容-->
<diskStore path="c:\Temp"/>
<!-- name随便起,timetoliveseconds存活时间 maxentrites最大的条数-->
<cache
name="users"
maxEntritesLocalHeap="1000"
timeToLiveSeconds="300"/>
<!-- eternal是否永久存储 timeToIdleSeconds允许空闲多久的数据被删除-->
<!-- eternal="true" 表示常驻缓存-->
<!-- overflowToDisk="false" 缓存数据太多了,不向磁盘存储,而是清除老的数据-->
<defaultCache
name="defaultCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpriyThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<!--memoryStoreEvictionPolicy 缓存的淘汰策略,最近最少使用 fifo最老数据-->
</ehcache>
3加入缓存管理
// 配置缓存管理
@Bean
public EhCacheManager getehacche() {
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml ");
return ehCacheManager;
}
在myrealm中设置这个缓存
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(GetMyRealm1());
defaultWebSecurityManager.setCacheManager(getehacche());
return defaultWebSecurityManager;
}
效果:
只执行一次权限查询了
session管理(15毫秒没有操作就退出)
可以粗略的理解为认证信息,是放在session中的,可以手动新建session,设置session的过期时间,这样就实现了用户登录后,多长没有进行操作,就自动退出登录。
1.需要自定义session管理器(15毫秒没有操作就退出)
@Bean
public DefaultWebSessionManager getdefaultWebSessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(15 * 1000);//单位是毫秒
return sessionManager;
}
2.在securitymanger中绑定这个session
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(GetMyRealm1());
defaultWebSecurityManager.setCacheManager(getehacche());
defaultWebSecurityManager.setSessionManager(getdefaultWebSessionManager());
return defaultWebSecurityManager;
}
Remember me 记住我
在关闭浏览器/关闭页面后,重新打开该页面,只要是登录过,不需要输入账号密码,直接登录。
shiro将用户对页面访问的权限分为三个级别:
未认证,可以访问的界面 login.html register.html
曾经认证过,可以访问的界面 个人基本信息
必须经过认证,才可以访问的界面 转账,必须登录才可以访问
1.设置自己的session
// 设置自己定义的session
@Bean
public DefaultWebSessionManager getdefaultWebSessionManager(){
DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
sessionManager.setGlobalSessionTimeout(15*1000);
System.out.println("执行了自己的session");
return sessionManager;
}
- 将自己定义的session管理器赋值给securitymanager
// sercuritymanager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setCacheManager(getehacche());
defaultWebSecurityManager.setRealm(MyRealm1());
defaultWebSecurityManager.setSessionManager(getdefaultWebSessionManager());
return defaultWebSecurityManager;
}
2.在shiroconfig中设置 曾经登录过可以访问的页面(user)
public ShiroFilterFactoryBean shiroFilter() {
// anon匿名用户可以访问
// authc认证用户可以访问
// user 使用了rememerme的可以访问
// perms对应的权限可以访问
// 对应的角色可以访问
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
filter.setSecurityManager(defaultWebSecurityManager());
Map<String, String> filterMap = new HashMap<>();
filterMap.put("/index", "user");//记住密码或者登录后才可访问
filterMap.put("/login.html", "anon");
filterMap.put("/register.html", "anon");
filterMap.put("/register", "anon");
filterMap.put("/static/**", "anon");
filterMap.put("/logintest", "anon");
filterMap.put("/**", "authc");
filterMap.put("/exit", "logout");
filterMap.put("/c_update", "perms[sys:c:update]");
filter.setFilterChainDefinitionMap(filterMap);
// 设置默认的登录页面
filter.setLoginUrl("/login.html");
// 设置未授权,但是访问了该页面就跳转到
filter.setUnauthorizedUrl("/lesspermission.html");
return filter;
}
3在shiroconfig中配置remembermemanager
@Bean
public CookieRememberMeManager getcookieRememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
SimpleCookie cookie = new SimpleCookie("rememberMe");
cookie.setMaxAge(30 * 24 * 60 * 90);
cookieRememberMeManager.setCookie(cookie);
return cookieRememberMeManager;
}
Securitymanager中添加这个类
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(GetMyRealm1());
defaultWebSecurityManager.setCacheManager(getehacche());
defaultWebSecurityManager.setSessionManager(getdefaultWebSessionManager());
defaultWebSecurityManager.setRememberMeManager(getcookieRememberMeManager());
return defaultWebSecurityManager;
}
- 前端需要添加一个单选框,是否记住密码 在验证用户登录的时候添加一句 usernamePasswordToken.setRememberMe(true 或者false); 即可
@Service
public class UserService {
public void checklogin(String username,String password,boolean rememberme) throws Exception{
System.out.println(rememberme+"rememberme");
Subject subject= SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);
usernamePasswordToken.setRememberMe(rememberme);
subject.login(usernamePasswordToken);
}
}
多realm
1多个不同的数据源 oracle mysql 多个数据源分别进行处理 存放的都是用户表,一个存了一部分,就需要每个数据源都要访问
1链式处理 就是配置两个realm
在配置security的过程中,添加这两个realm即可
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
Collection<Realm> realms=new ArrayList<>();
realms.add(getmyrealm2());
realms.add(getmyRealm());
defaultWebSecurityManager.setRealms(realms);
return defaultWebSecurityManager;
}
2多个数据源,选择一个进行验证 普通用户和管理员 一张用户表,一张管理员表,就是有两个realm,需要根据logintype判断到哪个realm进行认证。
分支处理 根据不同的条件,执行不同的realm
1。。因为官方的usernamepasswordtoken只有两个属性,用户名和密码,所以需要自己定义一个token,继承自usernamepasswordtoken
package com.example.shiro78.config;
import org.apache.shiro.authc.UsernamePasswordToken;
public class Mytoken extends UsernamePasswordToken {
private String loginType;
public Mytoken(String username, String password, String loginType) {
super(username, password);
this.loginType = loginType;
}
@Override
public String toString() {
return "Mytoken{" +
"loginType=" + loginType + getUsername()+"mytoken的值";
}
}
2。。因为官方的 将realm绑定到security的方法,是设置多少个realm,就绑定多少个,而我们要根据输入的logintype方法,确定要绑定哪个realm,所以要重写这个方法,即 MyModulaRealmAutherticator类中的doAuthenticate方法,就是写一个类,继承自MyModulaRealmAutherticator,然后重写doAuthenticate,这样该类其他的方法的代码都不会变,然后在sercuritymanager中将这个类设置为绑定的方法即可
package com.example.shiro78.config;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import java.util.Collection;
public class MyModulaRealmAutherticator extends ModularRealmAuthenticator {
System.out.println("执行了自己定义的doauthenticte方法");
Mytoken mytoken= (Mytoken) authenticationToken;
String logintype=mytoken.getLoginType();
Collection<Realm> realms = this.getRealms();
Collection<Realm> typerealms=new ArrayList<>();
for (Realm realm:realms)
{
if(realm.getName().startsWith(logintype))
{
System.out.println(realm.getName());
typerealms.add(realm);
}
}
if(typerealms.size() == 1)
{
return this.doSingleRealmAuthentication((Realm) typerealms.iterator().next(),authenticationToken);
}
else{
return this.doMultiRealmAuthentication(typerealms, authenticationToken);
}
将新的类注入ioc容器
@Bean
public MyModulaRealmAutherticator getMyModulaRealmAutherticator() {
MyModulaRealmAutherticator myModulaRealmAutherticator = new MyModulaRealmAutherticator();
return myModulaRealmAutherticator;
}
在sercuritymanager中将这个类设置为绑定的方法即可
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setAuthenticator(getMyModulaRealmAutherticator());
Collection<Realm> realms = new ArrayList<>();
realms.add(getmyrealm2());
realms.add(getmyRealm());
defaultWebSecurityManager.setRealms(realms);
return defaultWebSecurityManager;
}