定义: Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。
核心组件: Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
Realm 认证授权:
login 认证授权:
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>登陆</title>
<link href="js/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="css/login.css" rel="stylesheet" />
<script src="js/login.js" type="text/javascript"></script>
</head>
<body>
<div class="loginLayout">
<div class="loginHeader">
<!-- logo -->
<div class="login_logo"></div>
<!-- 竖线 -->
<img src="img/login_line.png"/>
<!-- 标题 -->
<div class="login_title">
<div class="title"></div>
<div class="title_2"></div>
</div>
</div>
<!-- welcome字符 -->
<div class="welcome_box">
<div class="login_welcome"></div>
</div>
<!-- 登录框 -->
<div class="loginBox">
<form action="./login/auth" method="post" onsubmit="loginFun(this)">
<div class="login_user">
<input type="text" placeholder="请输入用户名" id="userName" name="userName" onkeyup="this.value=this.value.replace(/\s+/g,'')" />
</div>
<div class="login_pass">
<input type="password" placeholder="请输入密码" id="password" name="password" onkeyup="this.value = this.value.replace(/[^\d.]/g,'')" />
</div>
<button type="submit">登录</button>
</form>
</div>
</div>
<script src="js/jquery-3.4.1/jquery-3.4.1/jquery-3.4.1.min.js" type="text/javascript"></script>
<script src="js/bootstrap-3.3.7-dist/js/bootstrap.min.js" type="text/javascript"></script>
</body>
</html>
Login controller
/**
* @Param: [request, response]
* @Return: void
* @Description: 登录
*/
@RequestMapping(value = "/login/auth", method = RequestMethod.POST)
public String loginPage(String userName, String password, HttpServletRequest request) throws IOException, ServletException {
User user = new User();
user.setFirmLoginName(userName);
user.setFirmLoginPassword(password);
// 根据用户名和密码创建token
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken//
(user.getFirmLoginName(), user.getFirmLoginPassword());
// 获取 subject 认证主体
Subject subject = SecurityUtils.getSubject();
try {
// 开始认证,这一步会跳到我们自定义的 Realm 中
subject.login(usernamePasswordToken);
User uses = userService.queryUser(userName);
request.getSession().setAttribute("users", uses);
if (uses.getFirmId().equals(HDAS_FIRM_USER_ID)) {
String adminPath = "/users/base.page";
return adminPath;
} else {
String path = "/users/demo.page";
return path;
}
} catch (Exception e) {
e.printStackTrace();
request.getSession().setAttribute("users", user);
request.setAttribute("error", "用户名或密码错误!");
return "redirect:/firstLogin.page";
}
}
Realm
/**
* @ClassName MyRealm
* @Description 自定义realm
* @Author Fetter
* @Date 2020/6/30 10:35
* @Version 1.0
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private UserService userService;
/**
* @Description: 授权
* @param:[principalCollection]
* @return:org.apache.shiro.authz.AuthorizationInfo
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取用户名
String username = (String) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 给该用户设置角色,角色信息存在 firm_role 表中取
authorizationInfo.setRoles(userService.getRoles(username));
// 给该用户设置权限,权限信息存在 firm_permission 表中取
authorizationInfo.setStringPermissions(userService.getPermissions(username));
return authorizationInfo;
}
/**
* @Description: 认证
* @param:[authenticationToken]
* @return:org.apache.shiro.authc.AuthenticationInfo
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 根据 Token 获取用户名,如果您不知道该 Token 怎么来的,先可以不管,下文会解释
String username = (String) authenticationToken.getPrincipal();
// 根据用户名从数据库中查询该用户
User user = userService.queryUser(username);
if(user != null) {
// 把当前用户存到 Session 中
SecurityUtils.getSubject().getSession().setAttribute("user", user);
// 传入用户名和密码进行身份认证,并返回认证信息
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getFirmLoginName(), user.getFirmLoginPassword(), "myRealm");
return authcInfo;
} else {
return null;
}
}
}
需要拦截认证授权的路径以及放行的资源
/**
* @ClassName ShiroConfig
* @Description 配置自定义realm
* @Author Fetter
* @Date 2020/6/30 10:45
* @Version 1.0
*/
@Configuration
public class ShiroConfig {
private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
/**
* 注入 Shiro 过滤器
* @param securityManager 安全管理器
* @return ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
// 定义 shiroFactoryBean
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
// 设置自定义的 securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 设置默认登录的 URL,身份认证失败会访问该 URL
shiroFilterFactoryBean.setLoginUrl("/firstLogin.page");
// 设置成功之后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/users/base.page");
// 设置未授权界面,权限认证失败会访问该 URL
// shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
// LinkedHashMap 是有序的,进行顺序拦截器配置
Map<String,String> filterChainMap = new LinkedHashMap<>();
// 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon 表示放行
filterChainMap.put("/static/**", "anon");
// 登录 URL 放行
filterChainMap.put("/firstLogin.page", "anon");
filterChainMap.put("/login/auth","anon");
filterChainMap.put("**/dust/home*/**", "anon");
filterChainMap.put("**/dust/parsingData*", "anon");
filterChainMap.put("**/dust/homeMapList*.json", "anon");
// “/user/admin” 开头的需要身份认证,authc表示要身份认证
filterChainMap.put("/login/auth*", "authc");
filterChainMap.put("/login/auth*/**", "authc");
filterChainMap.put("/users/base.page*/**", "roles[admin]");
filterChainMap.put("/users/index.page*/**", "authc");
filterChainMap.put("/users/demo.page*/**", "authc");
filterChainMap.put("/users/historyData*/**", "authc");
filterChainMap.put("/users/monitoraData*/**", "authc");
filterChainMap.put("/users/secretyKey*/**", "authc");
filterChainMap.put("/users/records*/**", "authc");
filterChainMap.put("/users/opsEntry*/**", "authc");
// 配置 logout 过滤器
filterChainMap.put("../logOut.page", "logout");
// 设置 shiroFilterFactoryBean 的 FilterChainDefinitionMap
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
return shiroFilterFactoryBean;
}
/**
* 注入自定义的 Realm
*
* @return MyRealm
*/
@Bean
public MyRealm myAuthRealm() {
MyRealm myRealm = new MyRealm();
return myRealm;
}
/**
* 注入安全管理器
*
* @return SecurityManager
*/
@Bean
public SecurityManager securityManager() {
// 将自定义 Realm 加进来
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(myAuthRealm());
return securityManager;
}
}