Shiro
Apache Shiro是一个功能强大且易于使用的 Java 安全框架,可执行身份验证、授权、加密和会话管理。
- Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
- Authorization:授权,即权限验证,判断某个已经认证过的用户是否拥有某些权限访问某些资源,一般授权会有角色授权和权限授权;
- SessionManager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的,web 环境中作用是和 HttpSession 是一样的;
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
它有三个核心的组件:Subject
, SecurityManager
和 Realms
.
-
Subject
:即当前的操作用户; -
SecurityManager
:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。 -
Realms
:Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
官方的十分钟入门文档:https://shiro.apache.org/10-minute-tutorial.html
以下为入门文档中的例子文件:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(securityManager);
//在几乎所有环境中,您都可以通过以下调用获取当前正在执行的用户:
Subject currentUser = SecurityUtils.getSubject();
//在用户与应用程序的当前会话期间向其提供内容,您可以获取他们的会话:(它不需要 HTTP 环境!)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
//对已知用户进行这些检查。我们Subject上面的实例代表当前用户,判断是否已经登录了
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
//如果登录尝试失败了怎么办,则会捕捉各种异常,并及时做出处理
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
catch (AuthenticationException ae) {
}
}
//输出已登录的用户信息:(测试角色)
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//测试其是否具有特定的作用:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//查看他们是否有权对某种类型的实体采取行动:(测试输入权限)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//查看用户是否有能力访问某个类型的特定实例:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//用户注销:
currentUser.logout();
System.exit(0);
}
}
小总结:
---认证:
1、subject获取用户; SecurityUtils.getSubject();
2、UsernamePasswordToken 封装数据
3、登录; subject.login(token);
之后就进行一些登录中的某些异常判断。
----拦截:分为三步
1、创建realm对象;
2、efaultWebSecurityManager
3、ShiroFilterFactoryBean;所有授权拦截操作都这里配置
导入shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
配置shiro
需要现有一个UserRealm:
package com.vxzx.config;
import com.vxzx.pojo.User;
import com.vxzx.server.UserServer;
import org.apache.shiro.SecurityUtils;
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.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServer userServer;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权的方法:====》AuthorizationInfo");
//将给用户进行授权,让用户具有一定的权限: SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//让所有用户都拥有user:add的权限:
// info.addStringPermission("user:add");
//拿到当前用户,让其自动识别数据库里面的拥有的权限:
Subject subject = SecurityUtils.getSubject();
//获取User:
User principal = (User) subject.getPrincipal();
//设置权限:
info.addStringPermission(principal.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证的方法:====》AuthenticationInfo");
UsernamePasswordToken userToken = (UsernamePasswordToken)authenticationToken;
//从数据库中获取用户信息:
User user = userServer.query(userToken.getUsername());
if (user==null){
return null;
}
//密码认证:
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
ShiroConfig:里面配置一些拦截的配置
package com.vxzx.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean:第三步(基于第二步)
@Bean
public ShiroFilterFactoryBean factoryBean(@Qualifier("securityManager")DefaultSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//关联第二步中的securityManager:
//设置安全管理器:
bean.setSecurityManager(defaultSecurityManager);
//添加shiro的内置过滤器:
/*
* anno : 无需认证就可以访问
* authc :必须认证才可以访问
* user : 必须拥有 记住我 才可以访问
* perms : 拥有对某个资源的权限
* roles : 拥有对某个角色的访问权
* */
Map<String, String> filters = new LinkedHashMap();
filters.put("/add","authc");
filters.put("/update","authc");
//授权:未授权的情况下,或跳转到未授权的页面去;
filters.put("/add","perms[user:add]");
filters.put("/update","perms[user:update]");
//过滤请求:
bean.setFilterChainDefinitionMap(filters);
//权限不够,跳转到专门页面去:
bean.setUnauthorizedUrl("/unauthorized");
//设置登录请求:
bean.setLoginUrl("/tologin");
//
return bean;
}
//DefaultWebSecurityManager: 第二步(基于第一步)
@Bean(name="securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("user")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联第一步中的userRealm:
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象:第一步
@Bean(name="user")
public UserRealm userRealm(){
return new UserRealm();
}
//整合ShiroDialect, 可以整合Shiro Thymeleaf
@Bean
public ShiroDialect shiroThy(){
ShiroDialect dialect = new ShiroDialect();
return dialect;
}
}
controller配置前端页面的跳转:
package com.vxzx.controller;
import com.vxzx.dao.UserMapper;
import com.vxzx.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class RouterC {
@Autowired
UserMapper userMapper;
@RequestMapping({"/","index"})
public String toIndex(Model model){
model.addAttribute("msg","Hello,Shiro");
return "index";
}
@RequestMapping("/add")
public String add(){
return "user/add";
}
@RequestMapping("/update")
public String update(){
return "user/update";
}
@RequestMapping("/tologin")
public String tologin(){
return "user/login";
}
@RequestMapping("/login")
public String login(String username,String password,Model model){
//获取当前正在执行的用户
Subject subject = SecurityUtils.getSubject();
//封装用户数据:
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名不正确");
return "/user/login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码不正确");
return "/user/login";
}
}
@RequestMapping("/unauthorized")
@ResponseBody
public String unauthorized(){
return "权限不够,无法访问!";
}
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "user/login";
}
}
其中还涉及到mybatis的配置及前端页面的配置,这些都自行配置就行,主要说明shiro的作用及配置。