Shiro安全框架spring 整合 SSM

文章概要:

  1. 环境搭建,配置文件
  2. shiro自定义的Realm
  3. 测试

 环境搭建,配置文件

ssm的环境搭建我的这篇文章介绍:ssm maven 工程目录以及环境搭建

现在是shiro和spring 的整合

在web.xml中在修改成,引入shiro.xml配置文件

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      classpath:spring/mybatis.xml
      classpath:spring/shiro.xml
    </param-value>
  </context-param>

shiro.xml,这边看到第一行提前配置了扫描器,因为shiro的realm需要用到service获取数据库中的角色和权限信息,也用到了ehcache缓存。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util.xsd">

    <context:component-scan base-package="com.ssmp.*" />
    <!-- 会话Session ID生成器 -->
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>

    <!--自定义Realm-->
    <bean id="shiroRealm" class="com.ssmp.realm.ShiroRealm">
        <!-- 加入了密码匹配器之后,就会默认将前台传递过来的密码自动MD5加密 -->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- 加密的方式 -->
                <property name="hashAlgorithmName" value="MD5" />
                <!-- 加密的次数,默认是1次 -->
                <property name="hashIterations" value="2"/>
            </bean>
        </property>
    </bean>

    <!--安全管理-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--领域(Realm)-->
        <property name="realm" ref="shiroRealm" />

        <!-- 缓存管理器 -->
        <property name="cacheManager" ref="cacheManager" />
    </bean>

    <!-- shiro的缓存管理器,然后需要将缓存管理器注入到安全管理其中  -->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!--classpath是缓存属性的配置文件  -->
        <property name="cacheManagerConfigFile" value="classpath:spring/EHCache.xml" />
    </bean>
    <!--shiro 过滤器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- Shiro过滤器的核心安全接口,这个属性是必须的-->
        <property name="securityManager" ref="securityManager"/>
        <!--身份认证失败,则跳转到登录页面的配置-->
        <property name="loginUrl" value="/login.jsp"/>
        <!--权限认证失败,则跳转到指定页面-->
        <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
        <!-- Shiro连接约束配置,即过滤链的定义-->
        <property name="filterChainDefinitions">
            <value>
                /login=anon
                /register=anon
                /admin=roles[admin]
                /user=perms[user:list]
                /**=authc
            </value>
        </property>
    </bean>
    <!-- Shiro生命周期处理器-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- AOP式方法级权限检查 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>

    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
</beans>

EHCache.xml,2.4版本以后需要一个defaultCache配置

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false"  name="shirocache">
    <diskStore path="java.io.tmpdir"/>
    <!-- 登录记录缓存 锁定10分钟 -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <cache name="shiro-activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
    <cache name="shiro_cache"
           maxElementsInMemory="2000"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           maxElementsOnDisk="0"
           overflowToDisk="true"
           memoryStoreEvictionPolicy="FIFO"
           statistics="true">
    </cache>
    <defaultCache
            maxElementsInMemory="10000"
            maxElementsOnDisk="0"
            eternal="true"
            overflowToDisk="true"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="0"
            diskSpoolBufferSizeMB="50"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LFU"
    />
</ehcache>

shiro自定义的Realm

package com.ssmp.realm;

import com.ssmp.entity.User;
import com.ssmp.service.ShiroService;
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.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private ShiroService shiroService;

    /**
     * 用户授权,当用户访问需要有权限的页面的情况,需要访问这个方法来获取权限列表
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获得已经登入过的用户名
        String username = (String) principalCollection.getPrimaryPrincipal();
        // 初始化权限信息
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 设置角色
        authorizationInfo.setRoles(shiroService.getRoles(username));
        // 设置权限
        authorizationInfo.setStringPermissions(shiroService.getPermissions(username));

        System.out.println(authorizationInfo.getRoles());
        System.out.println(authorizationInfo.getStringPermissions());
        return authorizationInfo;
    }

    //验证当前登录的用户,获取认证信息。
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //获取用户输入的用户名密码
        String username = (String) token.getPrincipal();
        // 获取数据库中保存的密码
        User user = shiroService.getUser(username);
        // 存在用户
        if (user != null ) {
            // 验证 token和info是否匹配
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()),getName());
        } else {
            // 当没有用户的时候,抛出异常
            throw new UnknownAccountException("用户名不存在");
        }
    }

    // 清楚缓存
    public void clearCached() {
        PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
        super.clearCache(principals);
    }
}

dao:

public interface UserMapper {
    @Insert(value = {"insert into user (username,password,salt)values(#{username},#{password},#{salt})"})
    int insert(User record);

    @Select(value = {"select * from user where username=#{username}"})
    User selectUser(String username);

    @Select(value = {"select * from user "})
    List<User> selectAllUser();

    @Select(value = {"select r.rolename from user u,role r,ur m where u.username=#{username} and u.id=m.uid and r.id=m.rid"})
    Set<String> selectRolesByUsername(String username);

    @Select(value = {"select p.permission from user u,ur a,permission p,rp b where u.username=#{username} and u.id=a.uid and a.rid=b.rid and b.pid=p.id"})
    Set<String> selectPermissionsByUsername(String username);

    @Insert(value = {"insert into rp (rid,pid) values(#{0},#{1})"})
    int insertRP(int rid,int pid);

    @Insert(value = {"insert into ur (uid,rid) values(#{0},#{1})"})
    int insertUR(int uid,int rid);
}

service:

@Service
public class ShiroServiceImpl  implements ShiroService {

    @Autowired
    private UserMapper userDao;

    @Autowired
    private ShiroRealm shiroRealm;

    @Override
    public User getUser(String username) {
        return userDao.selectUser(username);
    }

    @Override
    public List<User> getAllUser() {
        return userDao.selectAllUser();
    }

    @Override
    public Set<String> getRoles(String username) {
        return userDao.selectRolesByUsername(username);
    }

    @Override
    public Set<String> getPermissions(String username) {
        return userDao.selectPermissionsByUsername(username);
    }

    @Override
    public boolean register(String username, String password) {
        // 盐值
        String salt = new SecureRandomNumberGenerator().nextBytes().toString();
        // 生成的密码
        String encodedPassword = new SimpleHash("md5",password,salt,2).toString();
        // 载体
        User user = new User();
        user.setUsername(username);
        user.setPassword(encodedPassword);
        user.setSalt(salt);
        // 操作
        if(userDao.insert(user)==1)
            return true;

        return false;
    }

    @Override
    public boolean addRole(int uid, int rid) {

        if (userDao.insertUR(uid,rid)==1){
            shiroRealm.clearCached();
            return true;
        }
        return false;
    }

    @Override
    public boolean addPermission(int uid, int rid) {

        if (userDao.insertRP(uid,rid)==1){
            shiroRealm.clearCached();
            return true;
        }
        return false;
    }
}

测试:

数据库数据:

中间表:

实体类:

controller:

package com.ssmp.controller;

import com.ssmp.entity.User;
import com.ssmp.service.ShiroService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
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 org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
public class ShiroController {

    @Autowired
    private  ShiroService shiroService;

    @RequestMapping(value = "register",method = RequestMethod.POST)
    public String register(
            @RequestParam(name = "username")String username,
            @RequestParam(name = "password")String password){
        if(shiroService.register(username,password))
            return "success";
        return  "login";
    }
    @RequestMapping(value = "login",method = RequestMethod.POST)
    public String login(
            @RequestParam(name = "username")String username,
            @RequestParam(name = "password")String password){

        try{
            Subject subject = SecurityUtils.getSubject();
            if(subject.isAuthenticated()){
                System.out.println("------------用户已经授权,已经登录-----------");
            }
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            token.setRememberMe(true);
            subject.login(token);
            return "success";
        }catch (Exception e){
            e.printStackTrace();
        }
        return "login" ;
    }
    @RequestMapping("logout")
    public String logout(){
        //获取到Subject对象,然后退出
        SecurityUtils.getSubject().logout();
        return "login" ;
    }
    @RequestMapping("admin")
    public String admin(){
        return "admin" ;
    }
    @RequestMapping("user")
    @ResponseBody
    public List<User> user(){
        return shiroService.getAllUser() ;
    }
    @RequestMapping(value = "addRole",method = RequestMethod.POST)
    @ResponseBody
    public boolean addRole(
            @RequestParam(name = "uid")int uid,
            @RequestParam(name = "rid")int rid){
        return shiroService.addRole(uid,rid) ;
    }
    @RequestMapping(value = "addPermission",method = RequestMethod.POST)
    @ResponseBody
    public boolean addPermission(
            @RequestParam(name = "rid")int rid,
            @RequestParam(name = "pid")int pid){
        return shiroService.addPermission(rid,pid) ;
    }
}

用post访问:(未登录状态)访问admin和user都跳转到登录页面

  。。。

登录:

再次访问:显示了admin页面和user数据

。。。

大功告成,还是有小问题,例如缓存无法主动清除

猜你喜欢

转载自blog.csdn.net/qq_33683097/article/details/81674629