shiro分两篇文章写,应该分三篇来写,第一篇写各种概念和shiro执行流程,框架图这些的,但这些网上太多,我就不写了,主要侧重点在于实际应用。如果不看这个入门篇,进阶篇可能会看不懂,当然高手随便。文章中概念都是网上查阅后复制粘贴的,如有雷同,很是正常。代码为自己编写亲自测试,保证质量。此刻应有掌声~~
shiro是一个强大的简单易用的Java安全框架,主要用来更便捷的认证,授权,加密,会话管理。Shiro首要的和最重要的目标就是容易使用并且容易理解。
从大的角度来看,Shiro有三个主要的概念:Subject
,SecurityManager
,Realms后面还有个比较重要的filter需要配置,先了解一下这三个概念:
Subject:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,是一个比较抽象的概念,从代码角度来看比较容易理解。所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
3.1 首先shiro认证,授权不是集成了jar包就自己可以去认证,授权了,那样的话也太厉害了。所以首先先要告诉shiro是哪个用户登录上来了,然后再告诉shiro这个用户具有什么角色(角色来决定权限)。这些信息都给shiro了,shiro这个时候就会自动给你校验用户是否合法或是否有权限了。
3.1.1 如何告诉shiro是哪个用户呢,肯定是用户登录的时候告诉了。controller里面写了两个接口一个登录,一个测试,后续配置路径时就是这个接口路径。
3.1.2 上一步是告诉shiro用户来了,你开始工作了,那么怎么工作呢,shiro目前只知道登录的用户信息,如果验证是否合法肯定还要知道数据库的所有用户信息,此时它才能进行校验是否该用户存在或者合法。所以需要自定义AuthorizingRealm类,注意这个是必须要自定义的,不是我自己想去自定义的哈。
controller层
/**
* @author WYH
* @date 2019/3/19 11:30
*/
@RestController
@RequestMapping("user")
public class UserControll {
@GetMapping("login")
public Object login(){
User user = new User();
user.setUserName("wenyonghui");
user.setPassWord("123456");
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(),user.getPassWord());
Subject subject = SecurityUtils.getSubject();
subject.login(token);
User u = (User) subject.getPrincipal();
return getMessageMap(u);
}
@GetMapping("test01")
public Object test01(){
return getMessageTestMap("test01");
}
public Map getMessageMap(User user){
Map map = new HashMap();
map.put("code","0");
map.put("message","登录成功");
map.put("user",user);
return map;
}
public Map getMessageTestMap(String message){
Map map = new HashMap();
map.put("code","0");
map.put("message",message);
return map;
}
}
自定义AuthorizingRealm
/**
* @author WYH
* @date 2019/3/19 11:02
*/
public class UserRealm extends AuthorizingRealm {
/**
* @description 登录
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
//TODO 根据username从数据库中查询user数据,这里做模拟user实体。
User user = new User();
user.setUserName("wenyonghui");
user.setPassWord("123456");
Role role = new Role();
Set roleSet = new HashSet<>();
roleSet.add("admin");
role.setRole(roleSet);
user.setRole(role);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassWord(),getName());
System.out.println("开始认证");
return info;
}
/**
* @description 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set role = user.getRole().getRole();
info.setRoles(role);
return info;
}
}
3.1.3 完成上面两部其实已经差不多了,但此时只是做了告诉shiro登录的用户信息,和shiro从根据用户信息从数据库中查数据库中的用户信息,表面上看已经可以进行校验。但此时shiro还无法进行校验,因为项目中根本不知道你要用shiro来校验啊,校验规则是什么啊。所以此时需要对shiro进行配置,此类需要@Configuration注解,整体大环境是springboot,添加这个注解会自动装配,不多解释。
shirocongfiguration
package com.dx.shiro.shiro_deom.config;
import com.dx.shiro.shiro_deom.Test.RedisSessionDao;
import com.dx.shiro.shiro_deom.filter.CustomRolesAuthorizationFilter;
import com.dx.shiro.shiro_deom.usershiro.UserRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* shiro 配置类
* @author tianguifang
*
*/
@Configuration
public class ShiroConfiguration {
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myRealm());
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
@Bean
public DefaultWebSessionManager getSessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(getRedisSession());
sessionManager.setDeleteInvalidSessions(true);// 删除过期的session
sessionManager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session
return sessionManager;
}
@Bean
public UserRealm myRealm(){
return new UserRealm();
}
public CustomRolesAuthorizationFilter getRolesAuthorization(){
return new CustomRolesAuthorizationFilter();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map map = new HashMap<>();
map.put("rolesOrFilter",getRolesAuthorization());
shiroFilterFactoryBean.setFilters(map);
// 权限控制map.
Map filterChainDefinitionMap = new LinkedHashMap();
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/user/login","anon");
filterChainDefinitionMap.put("/user/test01","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* cookie对象;
* @return
*/
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//
simpleCookie.setMaxAge(60);
return simpleCookie;
}
/**
* cookie管理对象;记住我功能
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 默认AES算法 下面三行代码是用来获取秘钥的
// KeyGenerator keygen = KeyGenerator.getInstance("AES");
// SecretKey deskey = keygen.generateKey();
// System.out.println(org.apache.shiro.codec.Base64.encodeToString(deskey.getEncoded()));
cookieRememberMeManager.setCipherKey(Base64.getDecoder().decode("QxxW3vZvJtHjS4wknND81g=="));
return cookieRememberMeManager;
}
}
3.1.4 完成上面三部,一个基本的shiro登录和权限校验都完成了,是不是很简单。运行起来项目,可以先不登录直接访问test01接口试试,然后登录再访问test01。你会发现真的可以拦截和校验。
pom文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE
com.dx.shiro
shiro_deom
0.0.1-SNAPSHOT
shiro_deom
Demo project for Spring Boot
UTF-8
UTF-8
1.8
1.4.0-RC2
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-thymeleaf
net.sf.ehcache
ehcache-core
2.4.8
org.apache.shiro
shiro-core
${shiro.version}
org.apache.shiro
shiro-spring
${shiro.version}
org.apache.shiro
shiro-cas
${shiro.version}
org.apache.shiro
shiro-web
${shiro.version}
org.apache.shiro
shiro-ehcache
${shiro.version}
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.boot
spring-boot-maven-plugin
User实体类
package com.dx.shiro.shiro_deom.model;
import java.io.Serializable;
/**
* @author WYH
* @date 2019/3/19 11:20
*/
public class User implements Serializable {
private String userName;
private String passWord;
private Role role;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
Role实体类
package com.dx.shiro.shiro_deom.model;
import java.io.Serializable;
import java.util.Set;
/**
* @author WYH
* @date 2019/3/19 11:23
*/
public class Role implements Serializable {
private Set role;
public Role() {
}
public Set getRole() {
return role;
}
public void setRole(Set role) {
this.role = role;
}
}
下一篇写一下进阶篇,主要写:①.限制角色访问的路径,也就是特定的路径只能对特定的角色访问。②.一个用户拥有多个角色,不同角色又对应不同访问路径,此时该如何配置和处理。
(此文章为我工作几年来第一篇技术博客,以后会坚持写,此处做个标记,以便几年后回来看看如今的自己是多菜)