springboo+shiro+redis实现登录demo

项目结构:
springboo+shiro+redis实现登录demo_第1张图片
User实体类:

package com.shirodemo.bean;

import java.io.Serializable;

public class User implements Serializable {
    private int id;
    private String username;
    private String password;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

自定义sessionManager:

package com.shirodemo.common;


import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class MySessionManager extends DefaultWebSessionManager {

    private  static  final String AUTHORIZATION = "Authorization";//授权
    private  static  final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
        super();
    }

    /**
     *
     * @param request
     * @param response
     * @return
     * @decribe 重写getSessionId作为token,然后保存在redis里面,用户下次验证
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //获得请求头的token
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);

        //选择在ajax的请求头中传递sessionId,前后端分离选择在ajax请求头中获得sessionid
        if(!StringUtils.isEmpty(id))
        {

            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;

        }
        else
        {

            //默认是从cookie中得到sessionid
            return super.getSessionId(request, response);

        }



    }
}

ShiroConfig:

package com.shirodemo.config;

import com.shirodemo.Realm.UserRealm;
import com.shirodemo.common.MySessionManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.apache.shiro.mgt.SecurityManager;



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

//Shiro的配置类
@Configuration
public class ShiroConfig {

    /**
     * Shiro的核心api
     * Subject:用户主体(把操作交给SecurityManager)
     * SecurityManager:安全管理器(关联Realm)
     * Realm:Shiro连接数据的桥梁
     */






    /**
     * 创建ShiroFilterFactoryBean
     * @param
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager  securityManager) {

        ShiroFilterFactoryBean  shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//        Map filters = shiroFilterFactoryBean.getFilters();
//        filters.put("perms", new RestAuthorizationFilter());
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //配置未认证跳转页面


        //使用shiro内置过滤器拦截
        /**
         * 常用的过滤器
         *      anon:无须认证(登录)可以访问
         *      authc:必须认证才可以访问
         *      user: 如果使用remenberMe的功能才可以直接访问
         *      perms:该资源必须得到资源权限才可以访问
         *      role:该资源得到角色权限才可以访问
         */
        shiroFilterFactoryBean.setLoginUrl("/backLogin");
        //配置未授权跳转页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/unAthc");

        Map<String,String> filterMap = new LinkedHashMap<String,String>();
        filterMap.put("/login","anon");//拦截根目录
        filterMap.put("/swagger-ui.html","anon");
        filterMap.put("/static/**", "anon");
        filterMap.put("/swagger/**","anon");
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/swagger-resources/**","anon");
        filterMap.put("/v2/**","anon");
        filterMap.put("/img","anon");//文件资源放行
        filterMap.put("/doLogin","anon");//拦截根目录



        //资源授权例子
        //添加需要授权的资源
        filterMap.put("/role","perms[role:list]");
        filterMap.put("/user","perms[user:list]");
        filterMap.put("/user/addUser","perms[user:add]");//用户添加权限
        filterMap.put("/user/updateUser","perms[user:update]");//用户更新权限


        filterMap.put("/*","authc");//拦截根目录
        //未登录,shiro应重定向到登录界面,此处返回未登录状态信息由前端控制跳转页面




        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;
    }




    @Bean
    public DefaultWebSessionManager sessionManager()
    {
        //DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
        MySessionManager mySessionManager = new MySessionManager(); //使用自定义的session管理器
        mySessionManager.setCacheManager(cacheManager()); //设置缓存管理器
        mySessionManager.setSessionDAO(redisSessionDAO());//设置sessionDAO
        mySessionManager.setSessionIdUrlRewritingEnabled(true);
        return  mySessionManager;
    }

    /**
     * 创建Realm,需要自定义
     * @return
     */
    @Bean(name = "userRealm")
    public UserRealm getRealm()
    {
        UserRealm  userRealm = new  UserRealm();
        //userRealm.setCredentialsMatcher(credentialsMatcher());
        userRealm.setCachingEnabled(true); //开启缓存

        return userRealm;
    }


    /**
     * 盐值加密
     * @return
     */
//    @Bean(name="credentialsMatcher")
//    public HashedCredentialsMatcher credentialsMatcher()
//    {
//        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//
//        hashedCredentialsMatcher.setHashAlgorithmName("MD5");  //加密算法名
//
//        hashedCredentialsMatcher.setHashIterations(2); //加密次数
//
//        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); //采用hash散列算法加密
//
//        return hashedCredentialsMatcher;
//    }


    /**
     * 安全管理器
     * @param userRealm
     * @return
     */

    @Bean(name = "securityManager")
    public SecurityManager  getDefaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //配置自定义session管理,使用redis
        securityManager.setSessionManager(sessionManager());

        //配置缓存,使用redis
        securityManager.setCacheManager(cacheManager());


        securityManager.setRealm(getRealm());

        return  securityManager;
    }

    /**
     * 生命周期处理器
     * @return
     */
    @Bean(name="lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }



    /**
     *
     * redisManager管理器
     * @return
     */
    @Bean
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        //redis配置
        redisManager.setHost("127.0.0.1");
        redisManager.setPort(6379);
        redisManager.setPassword("123456");
        return redisManager;
    }

    /**
     * shiro缓存管理器,
     *
     * @return
     */
    public RedisCacheManager cacheManager()
    {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setPrincipalIdFieldName("username");//根据用户名缓存
        redisCacheManager.setExpire(200000);//设置缓存时间
        return  redisCacheManager;

    }

    /*
     * session dao
     * @return
     */
    @Bean
    public RedisSessionDAO redisSessionDAO()
    {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return  redisSessionDAO;
    }







}



登录接口:



package com.shirodemo.controller;

import com.alibaba.fastjson.JSONObject;
import com.shirodemo.bean.User;
import org.apache.shiro.SecurityUtils;
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.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {


    @PostMapping("/doLogin")
    public  String doLogin(@RequestBody User user)
    {


        JSONObject jsonObject = new JSONObject();
        Subject subject = SecurityUtils.getSubject();
        //根据用户名和密码生成token
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());

        try{
            subject.login(usernamePasswordToken);
            jsonObject.put("code",200);
            jsonObject.put("msg","登录成功");
            jsonObject.put("token",subject.getSession().getId());//把token发送给前端
        }
        catch (IncorrectCredentialsException e)
        {
            jsonObject.put("msg","密码错误");
        }
        catch (LockedAccountException e)
        {
            jsonObject.put("msg","用户账号已被冻结");
        }
        catch (UnknownAccountException e)
        {
            jsonObject.put("msg","该用户不存在");
        }

        return jsonObject.toJSONString();
    }

    @GetMapping(value= "/unAthc")
    public String unAthc()
    {
        System.out.println("跳转到未授权页面");
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("mycode","403");
        jsonObject.put("msg","未授权");

        return jsonObject.toJSONString();
    }

}

UserMapper:

package com.shirodemo.dao;

import com.shirodemo.bean.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {

    User getUserByUsername(String username);
}

UserService:

package com.shirodemo.service;

import com.shirodemo.bean.User;

public interface UserService {

    User getUserByUsername(String username);
}

UserServiceImpl

package com.shirodemo.service.impl;

import com.shirodemo.bean.User;
import com.shirodemo.dao.UserMapper;
import com.shirodemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class UserServiceImpl implements UserService {

    @Autowired
    UserMapper userMapper;
    @Override
    public User getUserByUsername(String username) {
        return userMapper.getUserByUsername(username);
    }
}

自定义UserRealm

package com.shirodemo.Realm;

import com.shirodemo.bean.User;
import com.shirodemo.service.UserService;
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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;


import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class UserRealm  extends AuthorizingRealm {

    @Autowired
    UserService tbUserService;

    /**
     *
     * @param principalCollection
     * @return
     * @describe执行授权逻辑
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        //给当前登录用户授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        Subject subject = SecurityUtils.getSubject();
//        //得到User
//        User tbUser =  (User)subject.getPrincipal();
//        //我这里是从数据库中得到权限字符
//        //例如我数据库中保存的是user:list这样的字符
//        System.out.println(tbUser);
//        List authority = tbUserService.getAuthorityByName(tbUser.getUsername());
//        System.out.println(authority);
//        /**
//         * 1:先查出用户名
//         * 2:根据用户名查询出用户id
//         * 3:根据用户id查询出role_id
//         * 4:根据role_id查询出perms_id
//         * 5:根据perms_id查询具体权限
//         * 6:然后封装在一个set中进行授权处理
//         */
//        Set set  = new HashSet();
//        for(String perms : authority)
//        {
//            if(null != perms)
//                set.add(perms);
//        }
//
//        info.setStringPermissions(set);

        return info;
    }

    /**
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     * @decribe 执行认证(登录)逻辑
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证(登录)逻辑");


        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //System.out.println(token.getUsername());
        //需要加密和解密
        User tbUser= (User)tbUserService.getUserByUsername(token.getUsername());

        //System.out.println(tbUser);

        String password = tbUser.getPassword();


        //这里直接拿Username生成盐值,也可以自定义
        //ByteSource credentialsSalt = ByteSource.Util.bytes(tbUser.getUsername());



        if(("").equals(password))
        {
            // throw new AuthenticationException();
            return null; //shiro底层会返回一个UnknownAccountException
        }

        return new SimpleAuthenticationInfo(tbUser,tbUser.getPassword(),getName());
    }
}

yml:

server:
  port: 8099

spring:
  profiles:
    active: dev

druid:
  login:
    username: root
    password: root



---
spring:
  profiles: dev
  datasource:
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password : root
    type: com.alibaba.druid.pool.DruidDataSource
    #连接池配置
    # 初始化大小,最小等待连接数量,最大等待连接数量,最大连接数
    initialsize: 1
    minidle: 1
    maxidle: 5
    maxActive: 20
    #最长等待时间
    maxWait: 6000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: true
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: false
    maxPoolPreparedStatementPerConnectionSize: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,log4j
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
    # 合并多个DruidDataSource的监控数据
    #spring.datasource.useGlobalDataSourceStat=true



---



mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.shirodemo.bean
pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  pageSizeZero: false #pageSize=0

---

spring:
  redis:
    port: 6379
    database: 0
    host: 127.0.0.1
    password: 123456
    #默认为没有密码
    jedis:
      pool:
        #连接池最大连接数
        max-active: 8
        #连接池最大阻塞时间(-1为没有限制)
        max-wait: -1
        #连接池最大空闲连接
        max-idle: 5
        #连接池最少空闲连接
        min-idle: 0
    timeout: 10000




结果:
在这里插入图片描述

redis缓存:
在这里插入图片描述

你可能感兴趣的:(springboot)