
学习视频链接:小狂神Springboot
鸟欲高飞先振翅,人求上进先读书——李苦禅
阿帕奇的安全框架
Apache Shiro是一个java的安全管理框架,可以用在javaEE环境下,也可以用在javaSE环境下。
此前我们学习了很多有关阿帕奇的东西:maven,tomcat,等等
官方号称十分钟就可以入门,
官网:https://shiro.apache.org/
为什么学他?:
(1)spring security 功能完善,学习成本偏高; (2)shiro 学习成本低,简单的安全框架,基本功能存在(登录认证,权限认证); (3)spring mvc interceptor(拦截器) 只能做登录认证,不能做权限认证。


Authentication:身份认证/登录;
Authorization:授权;
Session Manager:会话管理;
Cryptography:加密;
Web Support:**Web支持,可以非常容易的集成到Web环境;
Caching:缓存;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我。
Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro。


Subject:主体;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器;
Authrizer:授权器,;
Realm:可以有1个或多个Realm,是安全实体数据源;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;
SessionManager:**Shiro自己的Session来管理主体与应用之间交互的数据;
SessionDAO:**DAO大家都用过,数据访问对象,用于会话的CRUD;同时SessionDao也可以使用Cache进行缓存以提高性能。
CacheManager:缓存控制器,管理如用户、角色、权限等的缓存
Cryptography:密码模块
导入相关依赖:官方依赖
org.apache.shiro
shiro-core
1.6.0
org.slf4j
jcl-over-slf4j
1.7.21
org.slf4j
slf4j-log4j12
1.7.21
log4j
log4j
1.2.17 ${basedir}/src/main/webapp
${basedir}/src/main/resources
${basedir}/src/main/java
**/*.xml
**/*.properties
**/*.yml修改后的Quickstart类,
最新版本建议将:
FactorySecurityManager factory = new IniSecurityManagerFactory(classpath:shiro.ini);
SecurityManager securityManager = factory.getInstance();
转换位下面这样
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm(classpath:shiro.ini);
securityManager.setRealm(iniRealm);下面代码是Quickstart类:
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.text.IniRealm;
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;
/**
* Simple Quickstart application showing how to use Shiro's API.
*
* @since 0.9 RC2
*/
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We'll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
DefaultSecurityManager securityManager = new DefaultSecurityManager();
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
securityManager.setRealm(iniRealm);
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn't do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we'll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let's see what you can do:
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
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 + "]");
}
// let's login the current user so we can check against roles and permissions:
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);
}
} 三大对象:
常用过滤器如下
代码实战:
首先是shiroconfig,我们从下往上配置:
package com.hyc.config;
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.LinkedList;
import java.util.Map;
@Configuration
public class shrioconfig {
// shirofilterfactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/*
* 常用过滤器如下
* anon:无需认证访问
* authc:必须认证了才能访问
* user:记住我开启了,才可以用
* perms:拥有对某个资源的权限才能访问
* */
* */
Map<String,String> filter = new LinkedHashMap();
filter.put("/add","anon");
filter.put("/upd","authc");
bean.setFilterChainDefinitionMap(filter);
bean.setLoginUrl("/tologin");
return bean;
}
// dafultwebSecurityManager
@Bean(name="SecurityManager")
public DefaultWebSecurityManager getdefaultWebSecurityManager(@Qualifier("Userrealm") userrealm userrealm){
DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager();
// 关联Userrealm
SecurityManager.setRealm(userrealm);
return SecurityManager;
}
// 创建realm对象,需要自定义类
@Bean
public userrealm Userrealm() {
return new userrealm();
}
}realm对象需要引用外面的类 userrealm,我们需要继承AuthorizingRealm来获得授权,认证方法
package com.hyc.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class userrealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权=========>");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证=========>");
return null;
}
}我们需要去编写config类,设置权限,什么路径需要什么权限,
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/*
* 常用过滤器如下
* anon:无需认证访问
* authc:必须认证了才能访问
* user:记住我开启了,才可以用
* perms:拥有对某个资源的权限才能访问
role:该资源必须得到角色权限才可以访问
* */
Map<String,String> filter = new LinkedHashMap();
filter.put("/user/add","perms[user:add]");
filter.put("/user/upd","perms[user:upd]");
bean.setFilterChainDefinitionMap(filter);
bean.setLoginUrl("/tologin");
bean.setUnauthorizedUrl("/unauth");
return bean;
}之后去realm去认证,认证的信息是我们从数据库user表中查询出来的数据
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证=========>");
// 获取当前的用户
Subject subject = SecurityUtils.getSubject();
// 封装用户数据
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
user user = userService.queryUserByName(token.getUsername());
if (user==null){
return null;
}
//认证的时候创建用户登陆的session
Session session = subject.getSession();
//将用户的属性传入到session中
session.setAttribute("loginUser",user);
//如何让我们的user可以全局使用,我们需要设置info中第一个参数为user
return new SimpleAuthenticationInfo(user,user.getPassword() ,"");
}之后再认证之后获取用户对象授权,什么对象可以访问什么页面
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权=========>");
//授权信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//传递用户信息
Subject subject = SecurityUtils.getSubject();
user currentUser = (user) subject.getPrincipal();
//从数据库中获取授权角色
info.addStringPermission(currentUser.getParms());
info.addRole("user:add");
info.addRole("user:upd");
return info;
}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 (UnknownAccountException uae) {//用户名不存在
model.addAttribute("msg","用户名不存在");
return "login";
} catch (IncorrectCredentialsException ice) {//密码不存在
model.addAttribute("msg","密码错误");
return "login";
}
}这里有一个拓展可以做:就是密码加密处理
我们这里调用的login()方法会走上面我们配置的一系列流程
需要的命名空间
xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"要使用整合我们还需要导入整合包依赖
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0之后需要去配置类配置一个新的bean
//整合ShiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getshiroDialect(){
return new ShiroDialect();
}完成以上步骤就可以在模版引擎上使用shiro了
前端页面内容
Title
你好
登录
注销
add
updateshiroconfig
package com.hyc.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
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.LinkedList;
import java.util.Map;
@Configuration
public class shrioconfig {
// shirofilterfactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/*
* 常用过滤器如下
* anon:无需认证访问
* authc:必须认证了才能访问
* user:记住我开启了,才可以用
* perms:拥有对某个资源的权限才能访问
* */
Map<String,String> filter = new LinkedHashMap();
filter.put("/user/add","perms[user:add]");
filter.put("/user/upd","perms[user:upd]");
bean.setFilterChainDefinitionMap(filter);
bean.setLoginUrl("/tologin");
bean.setUnauthorizedUrl("/unauth");
return bean;
}
// dafultwebSecurityManager
@Bean(name="SecurityManager")
public DefaultWebSecurityManager getdefaultWebSecurityManager(@Qualifier("Userrealm") userrealm userrealm){
DefaultWebSecurityManager SecurityManager = new DefaultWebSecurityManager();
// 关联Userrealm
SecurityManager.setRealm(userrealm);
return SecurityManager;
}
// 创建realm对象,需要自定义类
@Bean
public userrealm Userrealm() {
return new userrealm();
}
//整合ShiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getshiroDialect(){
return new ShiroDialect();
}
}userrealm
package com.hyc.config;
import com.hyc.pojo.user;
import com.hyc.service.userServiceImpl;
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.session.Session;
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
userServiceImpl userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权=========>");
//授权信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//传递用户信息
Subject subject = SecurityUtils.getSubject();
user currentUser = (user) subject.getPrincipal();
//授权角色
info.addStringPermission(currentUser.getParms());
info.addRole("user:add");
info.addRole("user:upd");
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证=========>");
// 获取当前的用户
Subject subject = SecurityUtils.getSubject();
// 封装用户数据
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
user user = userService.queryUserByName(token.getUsername());
if (user==null){
return null;
}
Session session = subject.getSession();
session.setAttribute("loginUser",user);
return new SimpleAuthenticationInfo(user,user.getPassword() ,"");
}
}package com.hyc.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.session.ProxiedSession;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class Mycontroller {
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/upd")
public String upd(){
return "user/upd";
}
@RequestMapping("/tologin")
public String tologin(){
return "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 uae) {//用户名不存在
model.addAttribute("msg","用户名不存在");
return "login";
} catch (IncorrectCredentialsException ice) {//密码不存在
model.addAttribute("msg","密码错误");
return "login";
}
}
@ResponseBody
@RequestMapping("/unauth")
public String unauth(){
return "您没有权限";
}
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.removeAttribute("loginUser");
return "index";
}
}前端页面:
index
<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
>
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>你好</h1>
<span th:text="${msg}"></span>
<!--从session中判断值-->
<div th:if="${session.get('loginUser')==null}">
<a th:href="@{/tologin}">登录</a>
</div>
<a th:href="@{/logout}">注销</a>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}" >add</a>
</div>
<div shiro:hasPermission="user:upd">
<a th:href="@{/user/upd}">update</a>
</div>
</body>
</html>login
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>登陆</p>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}">
<p> 用户名:<input type="text" name="username"></p>
<p> 密码:<input type="password" name="password"></p>
<p><input type="submit" value="提交"></p>
</form>
</body>
</html>学习了Springsecurity和shiro之后,总结出一些学习方法