安全(权限框架)Shiro
官网: http://shiro.apache.org/
官方简介:
Apache Shiro™是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。 使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。
官方快速教程: https://github.com/apache/shiro/blob/master/samples/quickstart
import javax.security.auth.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class QuickerStart {
private static final transient Logger log = LoggerFactory.getLogger(QuickerStart.class);
public static void main(String[] args) {
// 固定三步
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 获取当前的用户对象
Subject currentUser = SecurityUtils.getSubject();
// 获取当前用户的session,并通过session进行存取值操作
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 + "]");
}
// 判断当前用户是否被认证
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 more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
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.");
}
//a (very powerful) Instance Level permission:
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!");
}
//all done - log out!
currentUser.logout();
System.exit(0);
}
}
1.Shiro架构
ApplicationCode---->Subject (当前用户)---->SecurityManager(管理所有用户,进行权限认证)--->Realm(连接安全数据)
2.shiro和spring的整合
-
进行shiro-spring依赖导入
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency>
-
config的编写,三步走
手动创建Realm类
public class UserRealm extends AuthorizingRealm { // 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } // 认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { return null; } }
config配置
@Configuration public class ShiroConfig { // 整体架构根据三部分组成 @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 设置安全管理器 factoryBean.setSecurityManager(defaultWebSecurityManager); // 添加内置过滤器 /* onon:无需认证就可以访问 authc:必须认证才能访问 user:必须拥有记住我工功能才可以使用 perms:拥有对某个资源的权限才可以访问(例如:管理员) role: 拥有某个角色权限才可以访问 */ // 进行页面访问认证,有点像druid日志权限控制 Map<String,String> filterMap = new LinkedHashMap<>(); filterMap.put("/add","authc"); // 进入add页面必须进行认证,如果没认证就会进入到登录认证的页面 filterMap.put("/select","authc"); filterMap.put("/select","perms[user:select]"); // 必须有user:select这样的权限才能访问 factoryBean.setFilterChainDefinitionMap(filterMap); // 设置登录认证页面 factoryBean.setLoginUrl("/tologin"); return factoryBean; } // DefaultWebSecurityManager @Bean(name="securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("UserRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 关联realm,上面进行了参数绑定 securityManager.setRealm(userRealm); return securityManager; } // 创建自定义的Realm对象 @Bean(name="UserRealm") public UserRealm userRealm(){ return new UserRealm(); } }
```java
// controller中进入到登录页面
@RequestMapping("/tologin")
public String tologin(){
return "login";
}
-
实现用户的认证
认证是在controller中接受到前端传递过来的信息,然后在Realm中进行账户密码的认证操作
1.controller中获取用户登录信息
@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 (IncorrectCredentialsException e){ // 这里可以抛出很多异常 model.addAttribute("msg","密码错误!"); return "login"; }catch (UnknownAccountException e){ model.addAttribute("msg","用户名不存在!"); return "login"; } }
2.在Realm中获取token进行数据库信息验证
// 认证和controller类是相互关联的 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 这里的token就是controller中的token,包含用户信息 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // 伪造数据库数据,正常连接数据库调用service方法,获取用户信息 String name = "zhangsan"; String password = "123"; // shiro中不会让我们去验证密码,所以只要验证用户名就好了 if (!token.getUsername().equals(name)){ return null; // 就会抛出controller中的异常 } return new SimpleAuthenticationInfo(user,password,""); // user是上传给授权的方法,用户获取用户权限 } }
-
进行用户授权认证,在config配置部分进行授权访问限制,principalCollection得到user的数据,判断用户是否有权限
// 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 1.对所有用户添加该权限 //authorizationInfo.addStringPermission("user:add"); // 2.获取当前登录对象,并获取认证方法返回值传递过来的user Subject subject = SecurityUtils.getSubject(); User currentuser= (User)subject.getPrincipal(); // 设置当前用户的权限 authorizationInfo.addStringPermission(currentuser.getXXX()); // currentuser.getXXX()用户在数据库表 中的权限 return authorizationInfo; }
3.shiro和thymeleaf整合
shiro和thymeleaf整合可以控制前端页面根据不同权限进行显示。不同权限的人和看到不同的页面
-
导入整合依赖
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.8</version> </dependency>
-
在shiro自定义配置文件中,也就是ShiroConfig,配置整合ShiroDialect
@Bean public ShiroDilect getShiroDilect(){ return new ShiroDilect(); }
-
前端操作
1.添加命名空间:跟springSecurity和相似
<html lang="en" xmlns:th="http://www.thymeleaf.org/" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
2.例子:用户是否有某个权限
<div shiro:hasPermisssion="user:add"> </div>