功能

  • 用户身份认证,权限授权、加密、会话管理等功能

image.png

认证、授权

添加依赖

  1. <dependency>
  2. <groupId>org.apache.shiro</groupId>
  3. <artifactId>shiro-spring-boot-web-starter</artifactId>
  4. <version>1.7.0</version>
  5. </dependency>

创建一个 Realm 类型的实现类

package com.cy.pj.sys.service.realm;

import com.cy.pj.sys.dao.SysMenuDao;
import com.cy.pj.sys.dao.SysUserDao;
import com.cy.pj.sys.pojo.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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 org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;

/**
 * 创建realm类型,并在此类型的对象中定义认证和授权数据获取逻辑
 */
@Slf4j
public class ShiroRealm extends AuthorizingRealm { //AuthorizingRealm继承了AuthenticatingRealm
    @Autowired
    private SysUserDao sysUserDao;

    @Autowired
    private SysMenuDao sysMenuDao;

    /**获取并封装授权信息,但不是在此方法中进行授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1.获取登录用户信息
        SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
        //2.基于登录用户获取用户权限
        Set<String> set = sysMenuDao.selectUserPermissions(user.getId());
        log.trace("set.size {}", set.size());
        log.trace("set.msg {}", set);
        //3.封装用户权限并返回
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(set);
        return info;
    } 

    /**负责获取并封装认证信息,认证具体的密码比对不在此方法*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取用户登录时提交的用户用户名
        UsernamePasswordToken uToken = (UsernamePasswordToken) authenticationToken;
        String username = uToken.getUsername();
        //2.基于用户名查询用户信息并校验
        SysUser user = sysUserDao.selectUserByUsername(username);
        if (user == null)
            throw new UnknownAccountException();
        if (user.getValid() == 0)
            throw new LockedAccountException();
        //3.封装查询到的用户信息并返回(交给调用者securitymanager)
        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt());
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                user, //principal 表示登录用户身份
                user.getPassword(), //hashedCredentials 数据库中已加密的密码
                credentialsSalt, //加密盐,ByteSource对象提供了对盐值的编码处理
                this.getName()); //realmName
        return info; //将封装好的数据交给securitymanager进行认证
    }

    /**获取凭证匹配器(加密策略),使用什么算法和策略对密码进行加密*/
    @Override
    public CredentialsMatcher getCredentialsMatcher() {
        //创建密码匹配器对象
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //设置加密算法
        matcher.setHashAlgorithmName("MD5");
        //设置加密次数
        matcher.setHashIterations(1);
        return matcher;
    }
}

注册Realm 对象配置

package com.cy.pj.sys.web.config;

import com.cy.pj.sys.service.realm.ShiroRealm;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
    /**配置Realm对象(org.apache.shiro.realm.Realm)
     * @Bean注解描述方法时,表示方法的返回值要交给spring管理。
     */
    @Bean
    public Realm realm(){
        return new ShiroRealm();
    }

    /**定义过滤规则(Shiro框架中提供了很多过滤器-Filter,它会对请求中的信息进行
     * 过滤,假如这些请求需要认证才可访问,则需要先登录认证,有些则可以直接访问)
     * ,例如在一些订票系统中,查询票信息不需要登陆,但是访问订单信息或进行订单
     * 创建时则需要登录,这些都称之为规则
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        //过滤链对象的定义(这个过滤链中包含了很多内置过滤器)
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        //指定过滤链中的过滤规则,例如:
        //配置/user/login/**开头的资源,可以匿名访问(不用登录就可以访问),其中anon为shiro框架指定的匿名过滤器
        chainDefinition.addPathDefinition("/user/login/**", "anon");
        //配置/user/logout,框架自带的登出
        chainDefinition.addPathDefinition("/user/logout","logout");
        //配置以/**开头的资源必须都要经过认证,其中authc为shiro框架指定的认证过滤器
        //chainDefinition.addPathDefinition("/**", "authc");
        //登录认证使用记住我功能,需将过滤器authc替换为user
        chainDefinition.addPathDefinition("/**", "user");
//        chainDefinition.addPathDefinition("/**","anon");
        return chainDefinition;
    }

    //配置session管理器对象
    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();
        //session的超时时间
        sessionManager.setGlobalSessionTimeout(1000*60*60);//1个小时
        //sessionManager.setGlobalSessionTimeout(2*60*1000);//2分钟
        //删除无效session
        sessionManager.setDeleteInvalidSessions(true);
        //当客户端cookie被禁用是否要设置url重写
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * 配置记住我管理器对象,此对象可以通过cookie对象存储账户信息,并将此信息
     * 写到客户端,下次客户端可以访问服务端时,可以携带cookie中的信息进行自动
     * 认证
     */
    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager cManager = new CookieRememberMeManager();
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(7*24*60*60);
        cManager.setCookie(cookie);
        //设置加密解密密钥
        //cManager.setCipherKey(Base64.decode("6ZmI6I2j5Y+R5aSn5ZOlAA=="));
        return cManager;
    }

    /**
     * 配置Shiro中自带的CacheManager对象,此对象可以将
     * 存储授权时封装的用户权限信息(SimpleAuthorizationInfo),下次再访问授权方法
     * 时不需要再到数据库查用户权限.
     * @return
     */
    @Bean
    protected CacheManager shiroCacheManager() {
        return new MemoryConstrainedCacheManager();
    }

    /**解决@RequiresPermissions注解使得访问controller方法失效问题*/
    @Bean //<bean id="" class="">
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator proxyCreator=
                new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);//让目标对象上的注解映射是生效
        //proxyCreator.setUsePrefix(true);
        return proxyCreator;
    }
}
  • 记住我功能要对token进行设置且将认证过滤器改成user

授权

  • @RequiresPermissions(“sys:user:update”) 表示只有拥有这种菜单权限的才可访问 ```java package com.cy.pj.sys.web.controller;

import com.cy.pj.common.pojo.JsonResult; import com.cy.pj.common.util.PageUtil; import com.cy.pj.sys.pojo.SysUser; import com.cy.pj.sys.service.SysUserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;

@RequestMapping(“/user/“) @RestController public class SysUserController { @Autowired private SysUserService sysUserService;

@GetMapping("login/{username}/{password}")
public JsonResult doLogin(@PathVariable String username,@PathVariable String password){
    System.out.println(username);
    //获取Shiro中的Subject对象,基于此对象提交用户信息
    Subject subject = SecurityUtils.getSubject();
    //执行登录(将用户名和密码提交给securityManager)
    UsernamePasswordToken token = new UsernamePasswordToken();
    token.setUsername(username);
    token.setPassword(password.toCharArray());
    token.setRememberMe(true);
    subject.login(token);
    return new JsonResult("login ok");
}

//@RequiredLog(operation = "查询用户信息")
//@RequiresPermissions("sys:user:view")
@GetMapping
public JsonResult doFindUsers(SysUser entity){
    return new JsonResult(PageUtil.startPage().doSelectPageInfo(()->{
        sysUserService.findUsers(entity);
    }));
}

@PostMapping
public JsonResult doSaveUser(@RequestBody SysUser entity){
    sysUserService.saveUser(entity);
    return new JsonResult("save ok");
}

@GetMapping("{id}")
public JsonResult doFindById(@PathVariable Integer id){
    return new JsonResult(sysUserService.findById(id));
}

@PutMapping
public JsonResult doUpdateUser(@RequestBody SysUser entity){
    sysUserService.updateUser(entity);
    return new JsonResult("update ok");
}

@RequiresPermissions("sys:user:update")
@PatchMapping("{id}/{valid}") //一部分数据的更新可使用Patch请求,当然也可使用Put
public JsonResult doValidById(@PathVariable Integer id,@PathVariable Integer valid){
    sysUserService.validById(id, valid);
    return new JsonResult("update valid success");
}

@PatchMapping("password/{sourcePassword}/{newPassword}/{confirmPassword}")
public JsonResult doUpdatePassword(@PathVariable String sourcePassword,@PathVariable String newPassword,@PathVariable String confirmPassword){
    sysUserService.updatePassword(sourcePassword,newPassword,confirmPassword);
    return new JsonResult("update password ok");
}

} ```