SpringBoot学习-(十七)SpringBoot整合Shiro

基本步骤:

  1. 添加pom文件依赖
  2. 书写自定义的realm
  3. 配置shiro
  4. 控制层使用

项目目录结构:

这里写图片描述


1.添加pom文件依赖

<!-- spring整合shiro -->
<!-- maven会自动添加shiro-core,shiro-web依赖 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
</dependency>

2.书写自定义realm

package com.ahut.shiro;

import java.util.ArrayList;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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;

/**
 * 
 * @ClassName: MyRealm
 * @Description: 自定义realm
 * @author cheng
 * @date 2017年10月9日 上午10:54:00
 */
public class MyRealm extends AuthorizingRealm {

    /**
     * 用于认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("使用了自定义的realm,用户认证...");
        System.out.println("用户名:" + ((UsernamePasswordToken) token).getUsername());
        System.out.println("密码:" + new String(((UsernamePasswordToken) token).getPassword()));

        // 模拟账号不存在
        // if (true) {
        // throw new UnknownAccountException();
        // }

        // 获取用户名
        String userName = (String) token.getPrincipal();
        // 依据用户名去数据库查询
        // 模拟从数据库中查询出来的散列值密码
        String password = "36f2dfa24d0a9fa97276abbe13e596fc";
        // 查询到了数据,验证密码是否正确
        // 密码正确,认证通过
        // 密码错误,认证失败
        // 没有查询到数据,认证失败

        // 模拟从数据库中获取salt
        String salt = "qwerty";

        // 与UsernamePasswordToken(userName, password)进行比较
        // 区别UsernamePasswordToken(userName, password)中的password是用户输入的密码,即没有加密过的密码
        // SimpleAuthenticationInfo(userName, password, ByteSource.Util.bytes(salt), this.getName())中的password是数据库中的密码,即加密过后的密码
        return new SimpleAuthenticationInfo(userName, password, ByteSource.Util.bytes(salt), this.getName());
    }

    /**
     * 用于授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("使用了自定义的realm,用户授权...");

        // 获取用户名
        // String userName = (String) principals.getPrimaryPrincipal();
        // 依据用户名在数据库中查找权限信息

        // 角色
        List<String> roles = new ArrayList<>();
        roles.add("admin");
        roles.add("user");
        // 权限
        List<String> permissions = new ArrayList<>();
        permissions.add("admin:select");
        permissions.add("admin:delete");

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissions);
        simpleAuthorizationInfo.addRoles(roles);
        return simpleAuthorizationInfo;
    }

}

3.配置shiro

package com.ahut.config;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ahut.shiro.MyRealm;

/**
 * 
 * @ClassName: ShiroConfig
 * @Description: spring整合shiro配置
 * @author cheng
 * @date 2017年10月10日 上午9:46:43
 */
@Configuration
public class ShiroConfig {

    /**
     * 
     * @Title: createMyRealm
     * @Description: 自定义的realm
     * @return
     */
    @Bean
    public MyRealm createMyRealm() {
        // 加密相关
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列次数
        hashedCredentialsMatcher.setHashIterations(2);
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return myRealm;
    }

    /**
     * 
     * @Title: securityManager
     * @Description: 注入自定义的realm
     * @Description: 注意方法返回值SecurityManager为org.apache.shiro.mgt.SecurityManager
     *               ,不要导错包
     * @return
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(createMyRealm());
        return securityManager;
    }

    /**
     * 
     * @Title: shirFilter
     * @Description: Shiro 的Web过滤器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/userLogin");
        // 登录成功后要跳转的链接,建议不配置,shiro认证成功自动到上一个请求路径
        shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权界面,指定没有权限操作时跳转页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        // 过滤器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被过滤的链接 顺序判断
        // 过虑器链定义,从上向下顺序执行,一般将/**放在最下边
        // 对静态资源设置匿名访问
        // anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/static/**", "anon");
        // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        // authc:所有url都必须认证通过才可以访问
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

}

代码说明:
1.自定义的realm和注入自定义的realm相当于以前的shiro.ini文件

#自定义的realm
myRealm=com.ahut.test.MyRealm
# 注入自定义的realm
securityManager.realms=$myRealm

2.ShiroFilterFactoryBean相当于以下xml配置

<!-- web.xml中shiro的filter对应的bean -->
<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
    <property name="loginUrl" value="/login.action" />
    <!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
    <property name="successUrl" value="/first.action"/>
    <!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
    <property name="unauthorizedUrl" value="/refuse.jsp" />
    <!-- 自定义filter配置 -->
    <property name="filters">
        <map>
            <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
            <entry key="authc" value-ref="formAuthenticationFilter" />
        </map>
    </property>

    <!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
    <property name="filterChainDefinitions">
        <value>
            <!-- 对静态资源设置匿名访问 -->
            /images/** = anon
            /js/** = anon
            /styles/** = anon
            <!-- 验证码,可匿名访问 -->
            /validatecode.jsp = anon

            <!-- 请求 logout.action地址,shiro去清除session-->
            /logout.action = logout
            <!--商品查询需要商品查询权限 ,取消url拦截配置,使用注解授权方式 -->
            <!-- /items/queryItems.action = perms[item:query]
            /items/editItems.action = perms[item:edit] -->
            <!-- 配置记住我或认证通过可以访问的地址 -->
            /index.jsp  = user
            /first.action = user
            /welcome.jsp = user
            <!-- /** = authc 所有url都必须认证通过才可以访问-->
            /** = authc
            <!-- /** = anon所有url都可以匿名访问 -->

        </value>
    </property>
</bean>

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="customRealm" />
    <!-- 注入缓存管理器 -->
    <property name="cacheManager" ref="cacheManager"/>
    <!-- 注入session管理器 -->
    <property name="sessionManager" ref="sessionManager" />
    <!-- 记住我 -->
    <property name="rememberMeManager" ref="rememberMeManager"/>    
</bean>

<!-- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
    <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
    <property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>

<!-- 凭证匹配器 -->
<bean id="credentialsMatcher"
    class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
    <property name="hashAlgorithmName" value="md5" />
    <property name="hashIterations" value="1" />
</bean>

<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>

<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
     <!-- session的失效时长,单位毫秒 -->
     <property name="globalSessionTimeout" value="600000"/>
     <!-- 删除失效的session -->
     <property name="deleteInvalidSessions" value="true"/>
</bean>

<!-- 自定义form认证过虑器 -->
<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
<bean id="formAuthenticationFilter" 
    class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
    <!-- 表单中账号的input名称 -->
    <property name="usernameParam" value="username" />
    <!-- 表单中密码的input名称 -->
    <property name="passwordParam" value="password" />
    <!-- 记住我input的名称 -->
    <property name="rememberMeParam" value="rememberMe"/>
</bean>

<!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
    <property name="cookie" ref="rememberMeCookie" />
</bean>

<!-- 记住我cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    <!-- rememberMe是cookie的名字 -->
    <constructor-arg value="rememberMe" />
    <!-- 记住我cookie生效时间30天 -->
    <property name="maxAge" value="2592000" />
</bean>

4.控制层使用

package com.ahut.action;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.ExpiredCredentialsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 
 * @ClassName: ShiroAction
 * @Description: shiro控制层
 * @author cheng
 * @date 2017年10月10日 上午10:18:21
 */
@RestController
public class ShiroAction {

    /**
     * 
     * @Title: userLogin
     * @Description: 用户登录
     * @return
     */
    @RequestMapping(value = "/userLogin")
    public String userLogin(String username, String password) {

        // 以下部分在配置阶段就已经完成,可以直接使用
        // 读取配置文件
        // Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        // 获取SecurityManager的实例
        // SecurityManager securityManager = factory.getInstance();
        // 把 securityManager 的实例绑定到 SecurityUtils 上
        // SecurityUtils.setSecurityManager(securityManager);

        System.out.println(username + ":" + password);
        Subject subject = SecurityUtils.getSubject();
        // 自己创建一个令牌,输入用户名和密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        try {
            subject.login(usernamePasswordToken);
            System.out.println("身份认证成功!");

        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("账号不存在!");

        } catch (LockedAccountException e) {
            e.printStackTrace();
            System.out.println("账号被锁定!");

        } catch (DisabledAccountException e) {
            e.printStackTrace();
            System.out.println("账号被禁用!");

        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("凭证/密码错误!");

        } catch (ExpiredCredentialsException e) {
            e.printStackTrace();
            System.out.println("凭证/密码过期!");

        } catch (ExcessiveAttemptsException e) {
            e.printStackTrace();
            System.out.println("登录失败次数过多!");

        }

        // 是否认证通过
        boolean isAuthenticated1 = subject.isAuthenticated();
        System.out.println("登录后,是否认证通过:" + isAuthenticated1);

        // 退出
        subject.logout();

        // 是否认证通过
        boolean isAuthenticated2 = subject.isAuthenticated();
        System.out.println("退出登录后,是否认证通过:" + isAuthenticated2);

        return "处理登录";
    }

}

可能出现的问题

页面加载css或者js时,出现以下错误:Resource interpreted as Stylesheet but transferred with MIME type text/html
原因:shiro配置原因,对需要认证的资源进行了拦截操作

// authc:所有url都必须认证通过才可以访问
filterChainDefinitionMap.put("/**", "authc");

修改以上代码为:
filterChainDefinitionMap.put("/**", "anon");

完整代码:

    /**
     * @description: shiro web过滤器
     * @author cheng
     * @dateTime 2018/4/18 15:50
     */
    @Bean
    public ShiroFilterFactoryBean createShiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/v1/toLoginPage");

        // 过滤器
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        // 配置不会被过滤的链接 顺序判断
        // 过虑器链定义,从上向下顺序执行,一般将/**放在最下边
        // 用户注册匿名访问
        filterChainDefinitionMap.put("/v1/users/", "anon");
        // 管理员登录页面
        filterChainDefinitionMap.put("/v1/toLoginPage", "anon");
        // 管理员登录
        filterChainDefinitionMap.put("/v1/login", "anon");
        // 对静态资源设置匿名访问
        // anon:所有url都都可以匿名访问
        filterChainDefinitionMap.put("/static/**", "anon");

        // authc:所有url都必须认证通过才可以访问
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值