功能
- 用户身份认证,权限授权、加密、会话管理等功能
认证、授权
添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.7.0</version>
</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");
}
} ```