shiro简单使用配置

目录

SpringBoot整合shiro框架

1.引入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.7.0</version>
</dependency>

yml文件中配置拦截后默认访问首页

shiro:
  loginUrl: /login.html

2.创建realm对象,并在此类型的对象中定义认证和授权数据的获取逻辑

 2.1.获取认证信息并封装
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;
//AuthorizingRealm(授权)继承AuthenticatingRealm(认证)
/**
 * 创建realm对象,并在此类型的对象中定义认证和授权数据的获取逻辑
 */
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    SysUsersDao sysUsersDao;

    /** 负责获取认证信息并封装  但是具体的密码比对不在此方法 是shiro框架底层帮我们完成*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.获取用户登录时提交的用户名
        UsernamePasswordToken uToken=(UsernamePasswordToken)authenticationToken;
        //获取用户名
        String name = uToken.getUsername();
        //2.基于用户名查询用户信息并校验
        //@Select("select * from sys_users where username=#{username}")
        //SysUsers selectUserByUsername(String username);
        SysUsers sysUsers = sysUsersDao.selectUserByUsername(name);
        //未知账户异常
        if(sysUsers==null)throw new UnknownAccountException();
                                        //锁定账户异常(账号禁用)
        if(sysUsers.getValid()==0)throw new LockedAccountException();
        //3.封装查询到的用户信息并返回(交给调用者)
                                   //ByteSource对盐做一个编码处理
        ByteSource credentialsSalt = ByteSource.Util.bytes(sysUsers.getSalt());
        SimpleAuthenticationInfo info =
                new SimpleAuthenticationInfo(
                        sysUsers,//principal 表示登录用户身份
                        sysUsers.getPassword(),//hashedCredentials 数据库中已加密的密码
                        credentialsSalt,//加密盐,ByteSource对盐值对象的处理
                        this.getName()//realmName
                );
        return info;//将封装好的数据交给securityManager进行认证
    }
}
2.2 获取凭证匹配器
 /*获取凭证匹配器(加密策略),使用什么算法和策略对密码进行加密*/
    @Override
    public CredentialsMatcher getCredentialsMatcher( ) {
        //创建密码匹配器对象
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //设置加密算法
        matcher.setHashAlgorithmName("MD5");
        //设置加密次数
        matcher.setHashIterations(1);
        return matcher;
    }
2.3 获取授权信息并封装(认证功能测试完毕再实现此操作)
/** 负责获取授权信息并封装 */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //1.获取登录用户信息
        SysUsers user= (SysUsers) principalCollection.getPrimaryPrincipal();
        //2.基于登录用户获取用户权限(根据用户id去数据库查询到当前用户权限)
        //@RequiresPermissions("sys:user:update")用在service实现类的方法上
        //框架会拿着set中的授权标识和注解中的参数比较
        //如果set中存在才能执行方法
        Set<String> set= sysMenuDao.selectUserPermissions(user.getId());
        //3.封装用户权限并返回
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.setStringPermissions(set);
        return info;
    }

3.创建shiro配置类

3.1返回刚刚自己创建的Realm对象
@Configuration
public class MyShiroConfig {
    /**
     * 配置ReaLm对象(org.apache.shiro.reaLm.ReaLm)
     */
    @Bean
    public Realm realm(){
        return new ShiroRealm();
    }
}
3.2 定义过滤规则 官网链接https://shiro.apache.org/web.html
 anon,logout,authc,user…这些是shiro框架指定的过滤规则
 (“/test1/**”, “anon”) anon 放行资源
 (“/test1/logout”,“logout”) logout 拦截进入退出页面
 (“/**”, “authc”); authc 对没有登录的用户拦截
 (设置记住我功能后 这里的authc改为user)
/*定义过滤规则(shiro框架中提供了很多过滤器-Filter,它会对请求中的信息进行过滤
        假如这些请求需要认证才可以访问,则需要先登录认证)
        比如订票时不用登录就能查询票信息,但是访问订单信息和订单创建时间就要登录
        可以用map设置多个过滤资源 chainDefinition.addPathDefinitions(map); */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
        //过滤链对象的定义(这个过滤链中包含了很多内置过滤器)
        DefaultShiroFilterChainDefinition chainDefinition=
                       new DefaultShiroFilterChainDefinition();
        //指定过滤链中的过滤规则,例如: (放行要在认证之前)
        //配置/test1/*开头的资源,可以匿名访问(不用登录),其中anon为shiro框架指定的认证过滤器
        chainDefinition.addPathDefinition("/test1/**", "anon");

        //配置登出,操作logout为shiro提供的一个默认的登出过滤器
        //(会默认跳转到login.html默认页面)
        chainDefinition.addPathDefinition("/test1/logout","logout");

        //配置以/**开头的资源必须都要认证,其中authc为shiro框架指定的认证过滤器
//        chainDefinition.addPathDefinition("/**", "authc");
        //设置记住我 这里的拦截配置就要把authc改为user
        chainDefinition.addPathDefinition("/**", "user");

        return chainDefinition;
    }
3.3 记住用户功能
 不配置setCipherKey是默认自己生成密钥对用户信息进行加密解密,这样的话你使用"记住我"时当你服务器重启就会再次重新生成密钥,用新的密钥解密之前cookie中的密钥就会报错无法解密
/**
     * shiro记住登录用户(记住我)
     * @return
     */
    @Bean
    public RememberMeManager rememberMeManager(){
        CookieRememberMeManager cManager=new CookieRememberMeManager();
        //默认记住用户信息 cookie的名字叫rememberMe
        SimpleCookie cookie=new SimpleCookie("rememberCGB");
        cookie.setMaxAge(7*24*60*60);//七天
        cManager.setCookie(cookie);
        //设置加密解密密钥
        //(不配置是默认自己生成密钥对用户信息进行加密解密,这样的话你使用"记住我"时当你服务器重启就会再次重新生成密钥,
        // 用新的密钥解密之前cookie中的密钥就会报错无法解密)

        //记住我的cookie会加密,加上此配置会固定用这个加密key加密和解密(Encode)
        cManager.setCipherKey(Base64.decode("6ZmI612j51+R5aSn5201AA=="));
        return cManager;

        /**生成Base64加密key(加密字符串)
         * KeyGenerator keyGenerator=KeyGenerator.getInstance("AES");
         * secretKey secretKey = keyGenerator.generateKey();
         * String key=Base64.encodeTostring( secretKey.getEncoded());
         * system.out.println(key);
         */
    }
3.4 配置session管理器对象
/**shiro配置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重写(默认为true)
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return  sessionManager;
    }
3.5 解决@RequiresPermissions用在controller层导致@GetMapper.@PostMapper…等注解失效的问题
/**
     * 此配置解决@RequiresPermissions("sys:user:update")加在controller
     *       会使@GetMapper.@PostMapper..这些注解失效的问题
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator proxyCreator=new DefaultAdvisorAutoProxyCreator();
        //下面两个方法都可以使注解生效
        proxyCreator.setProxyTargetClass(true);//让目标对象上的注解映射生效
//        proxyCreator.setUsePrefix(true);
        return proxyCreator;
    }

4.测试认证授权功能

 4.1 认证功能
 JsonResult是自己封装的类,用于服务端响应到客户端的数据(文章最后有添加这个类的代码)
@GetMapping("/{username}/{password}")
    public JsonResult doUpdatePassword(@PathVariable String username,
                                       @PathVariable String password){
        //获取shiro中的Subject对象,基于此对象提交用户信息
        Subject subject = SecurityUtils.getSubject();
        //执行登录(将用户名和密码提交给securityManager)
        UsernamePasswordToken token =
                    new UsernamePasswordToken(username, password);
        //用户登录时通过设置token对象设置记住我
        token.setRememberMe(true);
        subject.login(token);
        return new JsonResult("登录 ok");
    }
4.2 授权功能
 为service方法加@RequiresPermissions(“sys:user:update”)
 "sys:user:update"为shiro授权信息的固定格式
 /* @RequiresPermissions注解由shiro框架定义,基于此注解定义授权切入点方法,
     * 也就是说用户访问此方法时,需要授权才可访问。shiro框架底层会基于登录用户
     * 获取登录用户权限(权限标识),然后判定用户权限中是否包含@RequiresPermissions
     * 注解中定义的权限标识.
     *
     * 这个注解如果加载controller上,会导致@GetMapper.@PostMapper..这些注解失效(启动类117行解决此问题)
    .*/
    @RequiresPermissions("sys:user:update")
    public int validById(Integer id, Integer valid) {
        int rows=sysUserDao.validById(id,valid,"admin");//这里的admin为假数据,后续为登录用户
        if(rows==0)
            throw new RestClientException("记录可能不存在了");
        return rows;
    }
5.配置全局异常处理
/**
 * 对系统抛出的各种异常进行一个处理,这个时候就可以使用@ControllerAdvice注解了
 * @RestControllerAdvice其实就是在@ControllerAdvice加了一个@ResponseBody注解,用来将返回值写入到响应体
 *
  对哪个注解做一个拦截   可以传数组*/
@RestControllerAdvice(annotations = RestController.class)
public class RestControllerExceptionHandler {
   public static final Logger log =
           LoggerFactory.getLogger(RestControllerExceptionHandler.class);

    /**@ExceptionHandler异常处理器
     * 代表在被@RestController注解修饰的类中,RuntimeException,则有该方法来处理
     */
    @ExceptionHandler(RuntimeException.class)
    public JsonResult bdoHandleRuntimeException(RuntimeException e) {
        e.printStackTrace();
        log.error("exception msg is {}",e.getMessage());
        return new JsonResult(e);
    }

    public JsonResult doShiroException(ShiroException e){
        e.printStackTrace();
        JsonResult r=new JsonResult();
        r.setState(0);
        if (e instanceof UnknownAccountException){
            r.setMessage("账户不存在");
        }else if (e instanceof IncorrectCredentialsException){
            r.setMessage("密码不正确");
        }else if (e instanceof LockedAccountException){
            r.setMessage("账户被锁定");
        }else if (e instanceof AuthorizationException){
            r.setMessage("没有权限");
        }else{
            r.setMessage("认证或授权失败");
        }

        return r;
    }
}

JsonResult代码

/**
 * 通过此对象封装服务端响应到客户端的数据,让数据以一种规范化的格式呈现给客户端.
 */
public class JsonResult {
    /**状态码*/
    private Integer state=1;//1 表示OK,0表示Error
    /**状态码信息*/
    private String message="ok";
    /**封装正确的查询结果*/
    private Object data;

    public JsonResult(){}
    public JsonResult(String message){
        this.message=message;
    }
    public JsonResult(Integer state, String message){
        this(message);
        this.state=state;
    }
    public JsonResult(Object data){//new JsonResult(list)
        this.data=data;
    }
    //当出现异常时,可以通过此构造方法对异常信息进行封装
    public JsonResult(Throwable exception){//new JsonResult(exception);
        this(0,exception.getMessage());
    }
    public Integer getState() {
        return state;
    }

    public void setState(Integer state) {
        this.state = state;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {

        this.data = data;
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值