• * 项目环境搭建
  • * 配置ShiroConfig,用于shiro的基本配置和注入自定义规则
  • * 实现自定义的realm,继承AuthorizingRealm
  • * 编写测试controller和页面
  1. 基本环境准备
  2. 导入依赖坐标
  3. maven管理、shiro1.4.0 和spring-shiro1.4.0依赖
  4. 导入数据源,配置thymeleaf,redis,等等
  1. shiro配置
  2. 配置shiroConfig
  3. 编写自定义的realm
  4. 实现具体的doGetAuthorizationInfo(授权)方法和doGetAuthenticationInfo(认证)

具体实现:

shiroConfig配置

@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager ds = new DefaultWebSecurityManager();
ds.setRealm(myRealm);
return ds;
}
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultSecurityManager){
ShiroFilterFactoryBean sf = new ShiroFilterFactoryBean();
//设置安全管理器
sf.setSecurityManager(defaultSecurityManager);
sf.setLoginUrl("/login");
sf.setUnauthorizedUrl("/non");
sf.setSuccessUrl("/index");
/**
* 自定义过滤器
* anon:
* authc:
* user: 只有实现了remberme的操作才能访问
* perms: 必须得到资源权限才能访问
* role: 必须得到角色权限的时候才能访问
*/
Map<String,String> china = new LinkedHashMap<>();
china.put("/index","authc");
china.put("/update","authc");
china.put("/non","authc");
china.put("/toLogin","anon");
china.put("/add","perms[user:add]");
china.put("/**","anon");
sf.setFilterChainDefinitionMap(china);
return sf;
}
@Bean(name = "myRealm")
public MyRealm getRealm(){
return new MyRealm();
}

自定义编写realm 继承AuthorizingRealm 实现doGetAuthorizationInfo 和doGetAuthenticationInfo方法

/**
* 自定义授权逻辑
* Authorization
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//给资源授权
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//加上授权字符串(当前用户已经授权)
simpleAuthorizationInfo.addStringPermission("user:add");
return simpleAuthorizationInfo;
}
/**
* 自定义认证的逻辑
* 判断用户名和密码
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//直接强转
UsernamePasswordToken auth = (UsernamePasswordToken) authenticationToken;
System.out.println("处理用户登录逻辑");
char[] password = auth.getPassword();
String username = auth.getUsername();
if(!"user".equals(username)){
//返回null 会抛出 UnknownAccountException
return null;
}
//用户传入的密码 数据库中加载出来的密码
return new SimpleAuthenticationInfo(password,"123","");
}

 首先配置shiro的配置类使用@Configuration注解类上,这里面我们需要基本的三个配置类

    @Bean


    ShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultSecurityManager)


    @Bean(name = "securityManager")


    DefaultWebSecurityManager (@Qualifier("myRealm") MyRealm myRealm)


    @Bean(name = "myRealm")


    MyRealm()


    第一个是可以自定义认证授权规则,配置权限拦截规则指定跳转页面


    第二个是将shiro的安全管理器注入,然后返回


    第三种是自定义实现自己认证授权逻辑

自定义realm
继承自AuthorizingRealm,实现其中的两个方法
`protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)`

1. 此方法是授权逻辑的实现,指定用户可以拥有那些权限,将其set到addStringPermission集合中即可
2. 其中授权类是其的一个子类
3. SimpleAuthorizationInfo simpleAuthorizationInfo

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)

  此方式认证逻辑,用于判断用户是否登录成功,或者异常     
    用户名判断错误返回 null 
    密码使用new SimpleAuthenticationInfo(用户输入的密码,数据库的密码,"");

举个例子:

/**
* 自定义授权逻辑
* Authorization
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//给资源授权
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//加上授权字符串(当前用户已经授权)
simpleAuthorizationInfo.addStringPermission("user:add");
return simpleAuthorizationInfo;
} /**
* 自定义认证的逻辑
* 判断用户名和密码
*
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken auth = (UsernamePasswordToken) authenticationToken;
System.out.println("处理用户登录逻辑");
char[] password = auth.getPassword();
String username = auth.getUsername();
if(!"user".equals(username)){
//返回null 会抛出 UnknownAccountException
return null;
}
return new SimpleAuthenticationInfo(password,"123","");
}
/**
* 自定密码判断
*
* @param pass
* @return
*/
private boolean isPassWord(char[] pass){
String password = "123";
if(pass.length != password.length()){
return false;
}
char[] chars = password.toCharArray();
for(int i = 0; i < pass.length; i++){
if(chars[i] != pass[i]){
return false;
}
}
return true;
} // 当然授权也可以在controller层中通过@RequiresPermissions("user:update")注解在当前用户操作的地址上授权
<p style="color:red">
    注意: 这里面需要在shiroconfig中配置以下内容:
</p>
    
    /**
     * 解决@RequiresPermissions("XX:XXX:...")注解无效
     *
     * @return
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }     @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }     @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager defaultSecurityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(defaultSecurityManager);
        return advisor;
    }

  并且在controller里面配置的权限检验,用户验证失败,会跳转到/error页面上,需要自己自定义页面

注意:
这里面有个大坑,开始使用如下代码发现并未解决问题

    @Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties properties = new Properties();
properties.put("org.apache.shiro.authz.UnauthorizedException","/non");
simpleMappingExceptionResolver.setExceptionMappings(properties);
return simpleMappingExceptionResolver;
}

最后通过springMVC的异常处理类指定跳转页面,才解决此问题。

    @ControllerAdvice
public class MyExceptionHandler { @ExceptionHandler(UnauthenticatedException.class)
public String nauthenticatedException(Model model){
model.addAttribute("errorMsg","当前无用户!");
return "nonauth";
}
@ExceptionHandler(UnauthorizedException.class)
public String nauthorizedException(Model model){
model.addAttribute("errorMsg","当前用户没有权限");
return "nonauth";
}
}

不错
现在已经解决了两个问题:

1. 在用户未登录的时候,直接访问带有@RequiresPermissions权限的注解时会报错,而不是跳转到指定页面,或者返回相应的信息
2. 未使用在remal配置的权限过滤器的时候,而是在@Controller上直接带有@RequiresPermissions("user:add") 会报500错误,而不是可控操作

在doGetAuthenticationInfo方法中,获取用户登录时候的信息操作,验证用户,将用户存入session,以后通过subject对象强转。

     SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user,
user.getPassword(),
ByteSource.Util.bytes(salt),
//realm name
getName());

user 是开始通过用户名查询到的用户信息
    salt是加盐处理,硬编码加盐处理

#shiro密码匹配#
    
这里先来个简单的直接加盐 然后使用MD5加密方式
密码匹配则时先将用户输入的密码加盐再字符串比较

    实现自定义的Realm 继承自AuthorizingRealm

    doGetAuthenticationInfo方法中使用

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
用户对象
user,
数据库用户密码
user.getPassword(),
加盐处理
ByteSource.Util.bytes(salt),
//realm name
getName()); 将用户对象保存至
String string = MD5Util.encryptString(password+salt);
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userName,string);

使用shiro的HashedCredentialsMatcher自定义密码加密    
先模拟用户和密码数据 使用单元测试:

    @Test
public void contextLoads() {
String algorithmName = "md5";
String username = "admin";
String password = "123";
String salt1 = username;
String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();
int hashIterations = 3;
SimpleHash hash = new SimpleHash(algorithmName, password,
salt1 + salt2, hashIterations);
String encodedPassword = hash.toHex();
System.out.println(encodedPassword);
System.out.println(salt2);
}

将生成的数据保存到数据库中,验证的时候将密码和盐拿出来,这时候,用户的注册名和密码在注册的时候保存到数据库中

在Shiro的配置类中,注入

    /**
* 凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码;
* )
* @return
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new MyHashedCredent();
//使用指定的散列算法
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
//几次散列?
hashedCredentialsMatcher.setHashIterations(3);
//设置16进制编码
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}

  

在将其自定义的密码加载类注入到自定义的realm中

    @Bean(name = "myRealm")
public MyRealm getRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher){
MyRealm myRealm = new MyRealm();
// 设置自定义加密
myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return myRealm;
}

下面实现自定义密码:

    /**
* @author zhangyi
* @date 2018/12/12 20:43
*/
public class MyHashedCredent extends HashedCredentialsMatcher {
public MyHashedCredent(){}
/**
* 以后放到redis中保存
*/
private Cache<String, AtomicInteger> passwordRetryCache; public MyHashedCredent(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//限制每个用户的请求登录次数(密码错误的时候)
String username = (String) token.getPrincipal();
if(!Objects.isNull(passwordRetryCache)) {
// retry count + 1
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if (retryCount.incrementAndGet() > 5) {
// if retry count > 5 throw
throw new ExcessiveAttemptsException();
}
}
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
// clear retry count
// passwordRetryCache.remove(username);
}
return matches;
}
}

  

按照开涛神的方法,计算其密码重试的次数,将其保存到EhCache中,这里我保存到redis,因为好用吧。
开始使用硬编码加盐,网上说可能会有安全问题,现在采用
    
    用户名+密码+随机数 --> 在N次散列 加密,应该好一点点

我这里是继承了HashedCredentialsMatcher这中加密方式,还可以继承SimpleCredentialsMatcher,或者继承PasswordMatcher,或者实现CredentialsMatcher接口来加密,不过本质上差不多

SpringBoot整合shiro实现用户的认证授权的更多相关文章

  1. SpringBoot整合Shiro 四:认证+授权

    搭建环境见: SpringBoot整合Shiro 一:搭建环境 shiro配置类见: SpringBoot整合Shiro 二:Shiro配置类 shiro整合Mybatis见:SpringBoot整合 ...

  2. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期

    写在前面 通过前几篇文章的学习,我们从大体上了解了shiro关于认证和授权方面的应用.在接下来的文章当中,我将通过一个demo,带领大家搭建一个SpringBoot整合Shiro的一个项目开发脚手架, ...

  3. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理|前后端分离(下)----筑基后期

    写在前面 在上一篇文章<SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期>当中,我们初步实现了SpringBoot整合Shiro ...

  4. SpringBoot整合Shiro完成认证

    三.SpringBoot整合Shiro思路 首先从客户端发来的所有请求都经过Shiro过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如 ...

  5. SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)

    导入依赖(pom.xml)  <!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</group ...

  6. 补习系列(6)- springboot 整合 shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  7. SpringBoot系列十二:SpringBoot整合 Shiro

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Shiro 2.具体内容 Shiro 是现在最为流行的权限认证开发框架,与它起名的只有最初 ...

  8. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

  9. springboot整合Shiro功能案例

    Shiro 核心功能案例讲解 基于SpringBoot 有源码 从实战中学习Shiro的用法.本章使用SpringBoot快速搭建项目.整合SiteMesh框架布局页面.整合Shiro框架实现用身份认 ...

随机推荐

  1. 使用TensorFlow构建自己的网络

    TensorFlow对我来说,是一个陌生的框架,又很复杂,学起来不是很容易,需要找到合适的方法. 今天从tf.zeros入手,这个函数最简单,但是知道它的源码在哪里吗?后来我发现github上也有很多 ...

  2. cf 938E

    哇自闭了. 一样个毛啊. 和之前见过的几道感觉很类似啊. 首先一个数如果有贡献那么在他后面一定有一个大于它的数,并且前面的全比他小,然后我就跑偏了... 于是我们先排个序,显然无影响,我们可以考虑从 ...

  3. Strassen 矩阵相乘算法(转)

    偶尔在算法课本上面看到矩阵相乘的算法,联想到自己曾经在蓝桥杯系统上曾经做过一道矩阵相乘的题目,当时用的是普通的矩阵相乘的方法,效率极低,勉强通过编译.所以决定研究一下Strassen矩阵相乘算法,由于 ...

  4. 使用Bitsadmin 命令下载文件

    如果你碰到一个 Sa权限的注入点,你可以执行dos命令,但是你发现站库分离,数据库和web不在一个服务器上,而且悲剧的是数据库服务器又是个内网,这个时候你该怎么办? 这里就需要用到Bitsadmin来 ...

  5. Oracle课程档案,第二天

    salary:工资 order by:排序 desc:降序 hire:雇佣 单行函数 一周有七天 一月不一定只有30天 trunc:截取 dual:空表 last:最后 month:月份 round: ...

  6. js运用4

    ---恢复内容开始--- 1.函数    关键字function 复习 var  是js的关键字,用于声明变量,声明在内存模块完成,定义(=)是在执行模块完成. var可以在内存模块提前(js代码执行 ...

  7. ASP.NET MVC Routing Debugger路由调试工具

    官网地址:http://blog.csdn.net/sgear/article/details/6789882 To  use this, simply download the following ...

  8. BrowserRoute服务器配置

    BrowserRoute服务器配置 在React项目中我们经常需要采用React-Router来配置我们的页面路由,React-Router 是建立在 history 之上的,常见的history路由 ...

  9. 部署WEB项目到服务器(四)部署WEB项目Forum到linux服务器(Ubuntu)详解

    突发奇想,想在自己电脑上部署一个web网站. 1,使用Navicat for MYSQL客户端创建WEB项目数据库: Navicat for MYSQL连接虚拟机中的mysql数据库 启动mysql数 ...

  10. 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

    正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...