首先说一下shiro能干什么
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。(百度百科)
主要功能
三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
(文章最后附有本文的 密码加密算法 以及 本项目的demo资源-idea的)
目录
demo项目结构
pom文件引入依赖
ShiroConfigBean Shiro配置文件(java)
realms(*)
Ahandler
LoginHandler
service业务逻辑
exception全局异常捕捉
entity实体类
ehcache 进行缓存权限数据 配置
html代码
测试的话
对应代码资源 https://download.csdn.net/download/qq_32786139/10661538
本文密码加密算法
代码
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
org.springframework.boot
spring-boot-starter-test
test
log4j
log4j
1.2.17
org.aspectj
aspectjrt
1.8.13
org.aspectj
aspectjweaver
1.8.13
cglib
cglib
2.2.2
org.apache.shiro
shiro-core
1.2.2
org.apache.shiro
shiro-spring
1.2.2
org.apache.shiro
shiro-ehcache
1.2.2
org.springframework
spring-context-support
commons-io
commons-io
2.4
import com.example.demo.realms.MyShiroRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiro配置Bean
*/
@Configuration
public class ShiroConfigBean {
@Bean
public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器.
Map filterChainDefinitionMap = new LinkedHashMap();
// 设置login URL
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/LoginSuccess.action");
// 未授权的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized.action");
// src="jquery/jquery-3.2.1.min.js" 生效
filterChainDefinitionMap.put("/jquery/*", "anon");
// 设置登录的URL为匿名访问,因为一开始没有用户验证
filterChainDefinitionMap.put("/login.action", "anon");
filterChainDefinitionMap.put("/Exception.class", "anon");
// 我写的url一般都是xxx.action,根据你的情况自己修改
filterChainDefinitionMap.put("/*.action", "authc");
// 退出系统的过滤器
filterChainDefinitionMap.put("/logout", "logout");
// 现在资源的角色
filterChainDefinitionMap.put("/admin.html", "roles[admin]");
// filterChainDefinitionMap.put("/user.html", "roles[user]");
// 最后一班都,固定格式
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/*
* 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码; )
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));
return hashedCredentialsMatcher;
}
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 注入自定义的realm;
securityManager.setRealm(myShiroRealm());
// 注入缓存管理器;
securityManager.setCacheManager(ehCacheManager());
return securityManager;
}
/*
* 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持;
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
/*
* shiro缓存管理器;
* 需要注入对应的其它的实体类中-->安全管理器:securityManager可见securityManager是整个shiro的核心;
*/
@Bean
public EhCacheManager ehCacheManager() {
System.out.println("ShiroConfiguration.getEhCacheManager()");
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return cacheManager;
}
}
package com.example.demo.realms;
import com.example.demo.service.UserService;
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.util.ByteSource;
import java.util.HashSet;
import java.util.Set;
/**
* realm实现类,用于实现具体的验证和授权方法
* @author Bean
*
*/
public class MyShiroRealm extends AuthorizingRealm {
/**
* 方面用于加密 参数:AuthenticationToken是从表单穿过来封装好的对象
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("doGetAuthenticationInfo:" + token);
// 将AuthenticationToken强转为AuthenticationToken对象
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 获得从表单传过来的用户名
String username = upToken.getUsername();
// 从数据库查看是否存在用户
UserService userService = new UserService();
// 如果用户不存在,抛此异常
if (!userService.selectUsername(username)) {
throw new UnknownAccountException("无此用户名!");
}
// 认证的实体信息,可以是username,也可以是用户的实体类对象,这里用的用户名
Object principal = username;
// 从数据库中查询的密码
Object credentials = userService.selectPassword(username);
// 颜值加密的颜,可以用用户名
ByteSource credentialsSalt = ByteSource.Util.bytes(username);
// 当前realm对象的名称,调用分类的getName()
String realmName = this.getName();
// 创建SimpleAuthenticationInfo对象,并且把username和password等信息封装到里面
// 用户密码的比对是Shiro帮我们完成的
SimpleAuthenticationInfo info = null;
info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;
}
// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("MyShiroRealm的doGetAuthorizationInfo授权方法执行");
// User user=(User)
// principals.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
// System.out.println("在MyShiroRealm中AuthorizationInfo(授权)方法中从session中获取的user对象:"+user);
// 从PrincipalCollection中获得用户信息
Object principal = principals.getPrimaryPrincipal();
System.out.println("ShiroRealm AuthorizationInfo:" + principal.toString());
// 根据用户名来查询数据库赋予用户角色,权限(查数据库)
Set roles = new HashSet<>();
Set permissions = new HashSet<>();
// 2018.09.14更新
// 给用户添加user权限 (没有进行判断、对所有的用户给user权限)
if("user".equals(principal)){
roles.add("user");
permissions.add("user:query");
}
// 当用户名为admin时 为用户添加权限admin 两个admin可以理解为连个字段
if ("admin".equals(principal)) {
roles.add("admin");
permissions.add("admin:query");
}
// 为用户添加visit游客权限,在url中没有为visit权限,所以,所有的操作都没权限
if("visit".equals(principal)){
roles.add("visit");
permissions.add("visit:query");
}
// 更新以上代码
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//添加权限
info.setStringPermissions(permissions);
return info;
// return null;
}
}
handler(controller)
package com.example.demo.handler;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Ahandler {
// 登录的url
@RequestMapping({ "/login", "/" })
public String indexHtml() {
return "/index";
}
// 属于user角色@RequiresRoles("user")
// 必须同时属于user和admin角@RequiresRoles({ "user", "admin" })
// 属于user或者admin之一;修改logical为OR 即可@RequiresRoles(value = { "user", "admin"},
// logical = Logical.OR)
@RequestMapping("/showUserHtml.action")
@RequiresRoles(value = { "user", "admin"},logical = Logical.OR)
@RequiresPermissions("user:query")
public String userHtml() {
return "/user";
}
@RequestMapping("/showAdminHtml.action")
@RequiresRoles("admin")
@RequiresPermissions("admin:query")
public String adminHtml() {
return "/admin";
}
@RequestMapping("/unauthorized.action")
public String unauthorized() {
return "/abc";
}
@RequestMapping("/LoginSuccess.action")
public String listHtml() {
return "/list";
}
@RequestMapping("/error.action")
public String error() {
int a=1/0;
return "/abc";
}
}
package com.example.demo.handler;
import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
import java.util.Map;
/*
* 登陆的controller
*/
@Controller
public class LoginHandler {
@Autowired
private UserService userService;
@RequestMapping("/login.action")
public String login(String username, String password, Map map, HttpSession session) {
System.out.println(username + "---" + password);
// 获得当前Subject
Subject currentUser = SecurityUtils.getSubject();
// 验证用户是否验证,即是否登录
if (!currentUser.isAuthenticated()) {
String msg = "";
// 把用户名和密码封装为 UsernamePasswordToken 对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// remembermMe记住密码
token.setRememberMe(true);
try {
// 执行登录.
currentUser.login(token);
// 登录成功...
return "redirect:/LoginSuccess.action";
} catch (IncorrectCredentialsException e) {
msg = "登录密码错误";
System.out.println("登录密码错误!!!" + e);
} catch (ExcessiveAttemptsException e) {
msg = "登录失败次数过多";
System.out.println("登录失败次数过多!!!" + e);
} catch (LockedAccountException e) {
msg = "帐号已被锁定";
System.out.println("帐号已被锁定!!!" + e);
} catch (DisabledAccountException e) {
msg = "帐号已被禁用";
System.out.println("帐号已被禁用!!!" + e);
} catch (ExpiredCredentialsException e) {
msg = "帐号已过期";
System.out.println("帐号已过期!!!" + e);
} catch (UnknownAccountException e) {
msg = "帐号不存在";
System.out.println("帐号不存在!!!" + e);
} catch (UnauthorizedException e) {
msg = "您没有得到相应的授权!";
System.out.println("您没有得到相应的授权!" + e);
} catch (Exception e) {
System.out.println("出错!!!" + e);
}
map.put("msg", msg);
return "/index";
}
// 登录成功,重定向到LoginSuccess.action
return "redirect:/LoginSuccess.action";
}
}
package com.example.demo.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.example.demo.entity.User;
/**
* User的service,这里面的数据时静态数据,不查询数据库了
* @author lenovo
*
*/
@Service
public class UserService {
// 用户的集合
private List users = new ArrayList<>();
public UserService() {
// 从数据库查出来的用户名,密码,这是是静态数据(密码是123456)
users.add(new User("admin", "038bdaf98f2037b31f1e75b5b4c9b26e"));
users.add(new User("user", "098d2c478e9c11555ce2823231e02ec1"));
}
// 判断是否用户名是否存在
public boolean selectUsername(String username) {
for (User user : users) {
if (user.getUsername().equals(username)) {
return true;
}
}
return false;
}
// 根据用户返回查询的密码
public String selectPassword(String username) {
for (User user : users) {
if (user.getUsername().equals(username)) {
return user.getPassword();
}
}
return "";
}
}
package com.example.demo.exception;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 全局异常捕捉类
*
* @author Bean
*
*/
@ControllerAdvice
public class AllException {
/// 角色權权限异常捕捉
@ExceptionHandler(value = UnauthorizedException.class)
@ResponseBody // 在返回自定义相应类的情况下必须有,这是@ControllerAdvice注解的规定
public String roleException(UnauthorizedException e) {
System.out.println("---------------------->" + e);
return "角色权限不够!!!";
// return "/abc";
}
// 其它异常异常捕捉
@ExceptionHandler(value = Exception.class)
@ResponseBody // 在返回自定义相应类的情况下必须有,这是@ControllerAdvice注解的规定
public String allException(Exception e) {
System.out.println("---------------------->" + e);
return "系統出现异常!!!";
}
}
package com.example.demo.entity;
/**
* 实体类
* @author Bean
*
*/
public class User {
private Integer id;// 主键
private String username;// 用户名
private String password;// 密码
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
}
/**
* @return the id
*/
public Integer getId() {
return id;
}
/**
* @param id
* the id to set
*/
public void setId(Integer id) {
this.id = id;
}
/**
* @return the username
*/
public String getUsername() {
return username;
}
/**
* @param username
* the username to set
*/
public void setUsername(String username) {
this.username = username;
}
/**
* @return the password
*/
public String getPassword() {
return password;
}
/**
* @param password
* the password to set
*/
public void setPassword(String password) {
this.password = password;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User() {
}
}
-->
index.html 登陆页面
Insert title here
Login HTML
list.html 登陆成功页
Insert title here
list HTML
user html
admin html
error 1/0 html
退出
abc.html
Insert title here
abc HTML
admin.html
Insert title here
admin HTML
user.html
Insert title here
user HTML
以上就是所有demo代码了...
先使用admin登陆 密码123456 ,权限方面都能通过
再使用user登陆 密码123456 ,权限方面showAdminHtml.action没有访问权限
链接: https://pan.baidu.com/s/1wqXqxcMU-olevX27RPbHwg
提取码: vapf 复制这段内容后打开百度网盘手机App,操作更方便哦
//盐值用的用的是对用户名的加密(测试用的"lisi")
ByteSource credentialsSalt01 = ByteSource.Util.bytes("lisi");
Object salt = null;//盐值
Object credential = "123456";//密码
String hashAlgorithmName = "MD5";//加密方式
//1024指的是加密的次数
Object simpleHash = new SimpleHash(hashAlgorithmName, credential,
credentialsSalt01, 1024);
System.out.println("加密后的值----->" + simpleHash);
直接写个main方法测试就可以了