记录一下简单学习shiro的过程,希望对小伙伴们有效。
所有整合代码已更新到码云,对上次sprinboot+layui的练习进行了优化,加入了shiro安全框架,链接:
码云链接
1.shiro外部结构
2.shiro内部结构
3.快速开始shiro
官网教程Apache Shiro | Simple. Java. Security.
github:GitHub - apache/shiro: Apache Shiro
3.1导入maven
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Shiro uses SLF4J for logging. We'll use the 'simple' binding
in this example app. See http://www.slf4j.org for more info. -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
3.2 log4j.properties
##apache日志
log4j.logger.org.apache=WARN
##spring日志
log4j.logger.org.springframework=WARN
##shiro日志
log4j.logger.org.apache.shiro=INFO
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
3.3 shiro.ini
# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================
# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
3.4官方案例分析(Quickstart)
@SuppressWarnings("deprecation")
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
//固定代码,获取配置文件信息,利用工厂模式创建对象(与mybatis一样)
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
//设置单例模式
SecurityUtils.setSecurityManager(securityManager);
// 用户subject
// 重点:获取当前用户
Subject currentUser = SecurityUtils.getSubject();
// 重点:获取当前用户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()) {
//Token:令牌,没有获取,随机
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);
}
}
3.5 重要代码总结
// 重点:获取当前用户
Subject subject = SecurityUtils.getSubject();
subject.getPrincipal()
// 重点:获取当前用户shiro中的session(不是http里面session),存取值
Session session = currentUser.getSession();
//判断当前用户是否被验证
currentUser.isAuthenticated()
//权限判断
currentUser.hasRole()
4.springboot集成shiro
4.1导入坐标
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
<!-- thymeleaf-整合-shiro -->
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
4.2针对shiro三个重要类,编写配置类
@Configuration
public class ShiroConfig
{
@Autowired
DefaultWebSessionManager defaultSessionManager;
/*
* 固定代码(对应三个主要对象) Suject:用户 SecurityManager:管理所有用户 Realm:连接数据
*/
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//关联DefaultWebSecurityManager,设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
* anon: 无需认证就可以访问
* authc: 必须认证才能访问
* user: 必须拥有 记住我 功能才能用
* perms: 拥有对某个资源的权限才能访问
* role: 拥有某个角色权限才能访问
*
*/
//创建条件集合
Map<String, String> filterMap = new LinkedHashMap<>();
//授权:必须是user且拥有add权限
filterMap.put("/role/role-list", "perms[role:list]");
//授权:必须是user且拥有update权限
filterMap.put("/admin/admin-list", "perms[admin:list]");
//添加条件
// filterMap.put("/user/add", "authc");
// filterMap.put("/user/update", "authc");
filterMap.put("/role/*", "authc");
filterMap.put("/admin/*", "authc");
//设置登入请求
bean.setLoginUrl("/");
//设置未授权请求
bean.setUnauthorizedUrl("/noauth");
//添加条件集合
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
//DefaultWebSecurityManager @Qualifier("userRealm")绑定下面的userRealm()
@Bean(name="securityManager")
public DefaultWebSecurityManager getDafaultWebSecurityManage(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//关联UserRealm
defaultWebSecurityManager.setRealm(userRealm);
//设置DefaultWebSecurityManager的SessionManager,解决jsessionid问题
defaultWebSecurityManager.setSessionManager(defaultSessionManager);
return defaultWebSecurityManager;
}
//创建realm对象,需要自定义类
@Bean(name="userRealm")
public UserRealm userRealm() {
return new UserRealm();
}
//创建DefaultWebSessionManager类,并DI注入到IOC容器中,解决jsessionid问题
@Bean
public DefaultWebSessionManager mySessionManager(){
DefaultWebSessionManager defaultSessionManager = new DefaultWebSessionManager();
//将sessionIdUrlRewritingEnabled属性设置成false
defaultSessionManager.setSessionIdUrlRewritingEnabled(false);
return defaultSessionManager;
}
//注册ShiroDialect:thymeleaf-整合-shiro
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
public class UserRealm extends AuthorizingRealm
{
@Autowired
AcgUserServiceImpl acgUserService;
@Autowired
AcgRoleServiceImpl acgRoleService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了:授权doGetAuthenticationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//授权
//info.addStringPermission("user:add");
//从认证拿到当前用户
Subject subject=SecurityUtils.getSubject();
AcgUser currentUser = (AcgUser)subject.getPrincipal();
//授权
AcgRole acgRole = acgRoleService.getAcgRoleById(currentUser.getRole_id());
String[] roleList = acgRole.getRole_hasrole().split(";");
for(int i=0;i<roleList.length;i++) {
info.addStringPermission(roleList[i]);
}
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了:认证doGetAuthenticationInfo");
//这里的token就是前端传值封装的token,token是全局的
UsernamePasswordToken userToken=(UsernamePasswordToken) token;
//用户名 密码 ,数据库中取
AcgUser user = acgUserService.getAcgUserByUsername(userToken.getUsername());
if(user==null) {
return null;//抛出异常,UnknownAccountException
}
//密码认证,shiro做,避免密码泄露(这里密码默认加密了,也可以更换加密算法),第一个参数:传递对象授权,第二个参数:验证密码
return new SimpleAuthenticationInfo(user,user.getUser_password(),"");
}
}
4.3登录判断,注销判断,未授权
public String login(@RequestParam("username") String username, @RequestParam("pass") String pass, Model model,
HttpSession session) {
//shiro获取当前用户
Subject subject = SecurityUtils.getSubject();
//token封装用户登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,pass);
//执行登录:没有异常就登录成功
try {
subject.login(token);
session.setAttribute("username", username);
return "firstpage";
}
catch (UnknownAccountException e) {
model.addAttribute("msg", "用户名不存在!");
return "index";
}
catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误!");
return "index";
}
}
@RequestMapping("/outlogin")
public String outLogin(HttpSession session) {
if (session != null) {
session.removeAttribute("username");
}
//单例模式
Subject currentUser = SecurityUtils.getSubject();
//all done - log out!注销
//关闭shiro
currentUser.logout();
return "index";
}
//跳转未授权页面
@RequestMapping("/noauth")
@ResponseBody
public String Noauth() {
return "页面未授权!";
}
4.4前端页面
引入
<html xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
判断