• Shiro加入到Maven项目


    shiro加入到项目里面

    (1)创建一个maven模块

    (2)导入shiro的依赖包

            
            
                org.apache.shiro
                shiro-all
                1.4.1
            
            
            
                javax.servlet
                javax.servlet-api
                3.0.1
                provided
            
            
                org.springframework
                spring-web
                4.2.5.RELEASE
            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    引用:

    shiro 模块 -->pom.xml 引入server层

    	
    	        cn.dsq
    	        crm_service
    	        1.0-SNAPSHOT
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5

    web 模块 -->pom.xml 引入shiro层

      
                cn.dsq
                crm_shiro
                1.0-SNAPSHOT
            
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (3)在web.xml 配置代理过滤器

    
        shiroFilter
        org.springframework.web.filter.DelegatingFilterProxy
        
          targetFilterLifecycle
          true
        
      
    
    
       shiroFilter
       /*
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (4)新建一个文件applicationContext-shiro.xml

    
    
    
        
        
            
            
        
    
        
        
            
                
                    
                    
                
            
        
    
        
        
            
            
            
            
    
            
                
                    /login = anon
                    /** = authc
                
            
        
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    (5)在web.xml引入shiro的配置文件

    
      
        contextConfigLocation
        
            classpath:applicationContext.xml,
            classpath:applicationContext-shiro.xml
        
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    到这里就基本完成shiro加入到分模块开发的maven项目

    登录和登录之后访问数据

    用户密码加密

    1.员工密码加密保存

    1)已有员工测试类加密保存
    规定已有员工密码都是1,通过MD5Util工具加密后存放到数据库

    public class MD5Util {
    	/**盐*/
        public static final String SALT = "dsq";
    
        /**
         * 加密方法
         * @param source
         * @return
         */
        public static String encrypt(String source){
        	//加密方式-MD5  加密数据-source -  加盐-SALT 加密次数-10
            SimpleHash simpleHash = new SimpleHash("MD5",source,SALT,10);
            return simpleHash.toString();
        }
    	//测试法法
        public static void main(String[] args) {
            System.out.println(encrypt("1"));
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2)没有的员工添加是加密保存
    EmployeeController中

    @Override
       public AjaxResult addOrUpdate(Employee employee) {
       	//没有id表示是新增
           if (employee.getId()==null){
           	//密码进行加密
               employee.setPassword(MD5Util.encrypt(employee.getPassword()));
               //保存
               employeeService.add(employee);
           }else{
           	//修改
               employeeService.update(employee);
           }
           return AjaxResult.me();
       }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    登录实现

    1)login.vue

    handleSubmit2(ev) {
      var _this = this;
      this.$refs.ruleForm2.validate((valid) => {
        if (valid) {
          this.logining = true;
          var loginParams = { username: this.ruleForm2.account, password: this.ruleForm2.checkPass };
          this.$http.post("/login",loginParams).then(data => {
            this.logining = false;
            let { message, success, resultObj } = data.data;
            if (!success) {
              this.$message({
                message: message,
                type: 'error'
              });
            } else {
              //登录成功跳转/table的路由地址
              sessionStorage.setItem('user', JSON.stringify(resultObj));
              //修改登录成功后跳转到首页
              this.$router.push({ path: '/home' });
            }
          });
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2)LoginController

    @Controller
    @CrossOrigin
    public class LoginController {
        /**
         * 身份认证--登录
         * @param employee
         * @return
         */
        @RequestMapping(value = "/login",method = RequestMethod.POST)
        @ResponseBody
        public AjaxResult login(@RequestBody Employee employee){
            Subject currentUser = SecurityUtils.getSubject();
            if(!currentUser.isAuthenticated()){
    
                try {
                    UsernamePasswordToken token = new UsernamePasswordToken(employee.getUsername(),
                            employee.getPassword());
                    currentUser.login(token);
                } catch (UnknownAccountException e) {
                    e.printStackTrace();
                    return AjaxResult.me().setSuccess(false).setMessage("用户名不存在!");
                } catch (IncorrectCredentialsException e){
                    e.printStackTrace();
                    return AjaxResult.me().setSuccess(false).setMessage("密码错误!");
                } catch (AuthenticationException e){
                    e.printStackTrace();
                    return AjaxResult.me().setSuccess(false).setMessage("系统异常!");
                }
    
            }
            Employee employee1 = (Employee) currentUser.getPrincipal();
            employee.setPassword(null);
    
            //除了返回登录成功与否,还要把登录的用户返回前端
            return AjaxResult.me().setResultObj(employee1);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    由于前台需要返回用户,所以改造AJaxResult

    /**
     * Ajax请求的返回内容:增删改
     *    success:成功与否
     *    message:失败原因
     */
    public class AjaxResult {
    
        private boolean success = true;
        private String message = "操作成功!";
        private Object resultObj = null;
    
        public boolean isSuccess() {
            return success;
        }
    
        //链式编程,可以继续. 设置完成后自己对象返回
        public AjaxResult setSuccess(boolean success) {
            this.success = success;
            return this;
        }
    
        public String getMessage() {
            return message;
        }
    
        public AjaxResult setMessage(String message) {
            this.message = message;
            return this;
        }
    
        //默认成功
        public AjaxResult() {
        }
    
        //失败调用
        public AjaxResult(String message) {
            this.success = false;
            this.message = message;
        }
    
        public Object getResultObj() {
            return resultObj;
        }
    
        public AjaxResult setResultObj(Object resulObj) {
            this.resultObj = resultObj;
            return this;
        }
    
        //不要让我创建太多对象
        public static AjaxResult me(){
            return new AjaxResult();
        }
    
        public static void main(String[] args) {
            AjaxResult.me().setMessage("xxx").setSuccess(false);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    3)Realm

    /**
     * 自定义身份认证Realm
     */
    public class AuthenRealm extends AuthenticatingRealm {
    
        @Autowired
        private IEmployeeService employeeService;
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
            String username = token.getUsername();
            Employee employee = employeeService.getByUsername(username);
            if(employee==null){
                throw new UnknownAccountException(username);
            }
            Object principal = employee;
            Object hashedCredentials = employee.getPassword();
            ByteSource credentialsSalt = ByteSource.Util.bytes(MD5Util.SALT);
            String realmName = getName();
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,hashedCredentials,credentialsSalt,realmName);
            return info;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4)Service–Saas租户相关的,没有这个可以不管

    public interface IEmployeeService extends IBaseService {
        /**
         * 添加租户员工
         * @param employee
         */
        void addTenantEmployee(Employee employee);
    	/**
    	 * 通过username查询用户
    	 * @param username
    	 */
        Employee getByUsername(String username);
    }
    
    --------------------------------------------------------------------
    
    @Service
    public class EmployeeServiceImpl extends BaseServiceImpl implements IEmployeeService {
    
        @Autowired
        private TenantMapper tenantMapper;
    
        @Autowired
        private EmployeeMapper employeeMapper;
        
        @Override
        public void addTenantEmployee(Employee employee) {
        	//获取租户
            Tenant tenant = employee.getTenant();
            //设置租户注册时间
            tenant.setRegisterTime(new Date());
            //租户状态
            tenant.setState(0);
            //添加租户返回租户id   添加前对象里面没有id,添加完成后就有了
            tenantMapper.save(tenant);
            //把租户id设置给员工
            employee.setTenant(tenant);
            //在保存员工
            employee.setRealName(employee.getUsername());
            employeeMapper.save(employee);
        }
    
        @Override
        public Employee getByUsername(String username) {
            return employeeMapper.loadByUsername(username);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    5)Mapper

    /**
     * 通过继承baseMapper拥有的基础crud,还可以扩展自己方法
     */
    public interface EmployeeMapper extends BaseMapper {
        Employee loadByUsername(String username);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    mapper.xml

    
    	
    
    • 1
    • 2
    • 3
    • 4

    登录成功之后 无法查询数据。这个什么原因导致的

    cookie的管理机制导致

    原因分析

    前后端分离项目中,ajax请求没有携带cookie,所以后台无法通过cookie获取到SESSIONID,从而无法获取到session对象。而shiro的认证与授权都是通过session实现的,我们要想办法解决这个问题。
    在这里插入图片描述前后端需要建立会话机制
    通过token的机制建立前端和后端的会话管理机制
    在这里插入图片描述1)登录成功后返回token,并以后每次ajax请求都要携带token
    LoginController后台控制器

    		Employee employee1 = (Employee) currentUser.getPrincipal();
            employee.setPassword(null);
            Map result = new HashMap<>();
            result.put("user",employee1);
            System.out.println(currentUser.getSession().getId()+"xxxx"); 
            //登录成功后把会话id返回,会后作为token使用
            result.put("token",currentUser.getSession().getId());
    
            return AjaxResult.me().setResultObj(result);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Longin.vue前端登录页面

     this.$http.post("/login",loginParams).then(data => {
                  this.logining = false;
                  let { success, message, resultObj } = data.data;
                  if (!success) {
                    this.$message({
                      message: message,
                      type: 'error'
                    });
                  } else {
                    
                      //登录成功跳转/table的路由地址
                    sessionStorage.setItem('user', JSON.stringify(resultObj.user));
                    sessionStorage.setItem('token', resultObj.token); //不要加字符串转换了巨大的坑
                      //修改登录成功后跳转到首页
                    this.$router.push({ path: '/echarts' });
                  }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Home.vue前端主页

    		//退出登录
    		logout: function () {
    			var _this = this;
    			this.$confirm('确认退出吗?', '提示', {
    				//type: 'warning'
    			}).then(() => {
    				sessionStorage.removeItem('user');
    				sessionStorage.removeItem('token');
    				_this.$router.push('/login');
    			}).catch(() => {
    
    			});
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Main.js

    //拦截器 
    axios.interceptors.request.use(config => {
        if (sessionStorage.getItem('token')) {
            // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
            config.headers['X-Token'] = sessionStorage.getItem('token')
        }
        console.debug('config',config)
        return config
    }, error => {
        // Do something with request error
        Promise.reject(error)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2)服务端变为通过token来唯一标识session

    Shirospring配置文件
      
        
    
        
        
            
            
            
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    CrmSessionManager

    /**
     *
     * 传统结构项目中,shiro从cookie中读取sessionId以此来维持会话,
     * 在前后端分离的项目中(也可在移动APP项目使用),我们选择在ajax的请求头中传递sessionId,
     * 因此需要重写shiro获取sessionId的方式。
     * 自定义CrmSessionManager类继承DefaultWebSessionManager类,重写getSessionId方法
     *
     */
    public class CrmSessionManager extends DefaultWebSessionManager {
    
        private static final String AUTHORIZATION = "X-TOKEN";
    
        private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
    
        public CrmSessionManager() {
            super();
        }
    
        @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //取到jessionid
            String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
            HttpServletRequest request1 = (HttpServletRequest) request;
            //如果请求头中有 X-TOKEN 则其值为sessionId
            if (!StringUtils.isEmpty(id)) {
                System.out.println(id+"jjjjjjjjj"+request1.getRequestURI()+request1.getMethod());
                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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    跨域预检查放行 OPTIONS每次跨域

    cors跨域处理时,每次都要跨域预检查,也就是发一个options请求,这种请求shiro应该放行

    
    
    
    
    
        
        
            
            
            
            
            
                
                    
                
            
            
                
                    /login = anon
                    /** = myAuthc
                
            
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    MyAuthenticationFilter

    /**
    * 自定义身份认证过滤器
    */
    public class MyAuthenticationFilter extends FormAuthenticationFilter {
    
       @Override
       protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
           //如果是OPTIONS请求,直接放行
           HttpServletRequest httpServletRequest = (HttpServletRequest) request;
           String method = httpServletRequest.getMethod();
           System.out.println(method);
           if("OPTIONS".equalsIgnoreCase(method)){
               return true;
           }
           return super.isAccessAllowed(request, response, mappedValue);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    UserContext保存登录用户

    实现

    /**
     * 当前登录用户相关
     */
    public class UserContext {
        private static final String CURRENT_LOGIN_USER=  "loginUser";
    
        /**
         * 设置当前登录用户
         * @param employee
         */
        public static void setUser(Employee employee){
            Subject currentUser = SecurityUtils.getSubject();
            currentUser.getSession().setAttribute(CURRENT_LOGIN_USER,employee);
        }
    
        /**
         * 获取当前登录用户
         * @return  employee
         */
        public static Employee getUser(){
            Subject currentUser = SecurityUtils.getSubject();
            return (Employee) currentUser.getSession().getAttribute(CURRENT_LOGIN_USER);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    Prim求最小生成树
    【算法康复训练③】剑指offer P3
    浪潮信息InManage升级发布 三大功能释放数据中心运维管理压力
    linux信号==Linux应用编程5
    深度学习参数初始化(一)Xavier初始化 含代码
    LeetCode647:回文子串
    前后端分离中,前端请求和后端接收请求格式总结
    操作系统(二 )| 进程控制 进程状态 进程描述 进程控制 进程同步互斥
    咖啡屋时光书城【原创】
    正则表达式——2.正则表达式的基础
  • 原文地址:https://blog.csdn.net/m0_67402096/article/details/126496261